littlejsengine 1.2.3 → 1.2.9

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.
@@ -344,7 +344,13 @@ class Vector2
344
344
  /** Returns true if this vector is within the bounds of an array size passed in
345
345
  * @param {Vector2} arraySize
346
346
  * @return {Boolean} */
347
- arrayCheck(arraySize) { return this.x >= 0 && this.y >= 0 && this.x < arraySize.x && this.y < arraySize.y; }
347
+ arrayCheck(arraySize) { return this.x >= 0 && this.y >= 0 && this.x < arraySize.x && this.y < arraySize.y; }
348
+
349
+ /** Returns this vector expressed as a string
350
+ * @param {float} digits - precision to display
351
+ * @return {String} */
352
+ toString(digits=3)
353
+ { return `(${(this.x<0?'':' ') + this.x.toFixed(digits)},${(this.y<0?'':' ') + this.y.toFixed(digits)} )`; }
348
354
  }
349
355
 
350
356
  ///////////////////////////////////////////////////////////////////////////////
@@ -415,7 +421,7 @@ class Color
415
421
  * @return {Color} */
416
422
  lerp(c, p) { return this.add(c.subtract(this).scale(clamp(p))); }
417
423
 
418
- /** Sets this color given a hue, saturation, lightness , and alpha
424
+ /** Sets this color given a hue, saturation, lightness, and alpha
419
425
  * @param {Number} [hue=0]
420
426
  * @param {Number} [saturation=0]
421
427
  * @param {Number} [lightness=1]
@@ -451,21 +457,43 @@ class Color
451
457
  ).clamp();
452
458
  }
453
459
 
454
- /** Returns this color expressed as an rgba string
460
+ /** Returns this color expressed as an CSS color value
455
461
  * @return {String} */
456
- rgba()
462
+ toString()
457
463
  {
458
464
  ASSERT(this.r>=0 && this.r<=1 && this.g>=0 && this.g<=1 && this.b>=0 && this.b<=1 && this.a>=0 && this.a<=1);
459
465
  return `rgb(${this.r*255|0},${this.g*255|0},${this.b*255|0},${this.a})`;
460
466
  }
461
467
 
462
- /** Returns this color expressed as 32 bit integer value
468
+ /** Returns this color expressed as 32 bit integer RGBA value
463
469
  * @return {Number} */
464
470
  rgbaInt()
465
471
  {
466
472
  ASSERT(this.r>=0 && this.r<=1 && this.g>=0 && this.g<=1 && this.b>=0 && this.b<=1 && this.a>=0 && this.a<=1);
467
473
  return (this.r*255|0) + (this.g*255<<8) + (this.b*255<<16) + (this.a*255<<24);
468
474
  }
475
+
476
+ /** Returns this color expressed as a hex code
477
+ * @return {String} */
478
+ hex()
479
+ {
480
+ const toHex = (c)=> ((c=c*255|0)<16 ? '0' : '') + c.toString(16);
481
+ return '#' + toHex(this.r) + toHex(this.g) + toHex(this.b);
482
+ }
483
+
484
+ /** Set this color from a hex code
485
+ * @param {String} hex - html hex code
486
+ * @return {Color} */
487
+ setHex(hex)
488
+ {
489
+ const fromHex = (a)=> parseInt(hex.slice(a,a+2), 16)/255;
490
+ this.r = fromHex(1);
491
+ this.g = fromHex(3),
492
+ this.b = fromHex(5);
493
+ this.a = 1;
494
+ ASSERT(this.r>=0 && this.r<=1 && this.g>=0 && this.g<=1 && this.b>=0 && this.b<=1);
495
+ return this;
496
+ }
469
497
  }
470
498
 
471
499
  ///////////////////////////////////////////////////////////////////////////////
@@ -502,7 +530,7 @@ class Timer
502
530
 
503
531
  /** Returns true if set and elapsed
504
532
  * @return {Boolean} */
505
- elapsed() { return time > this.time; }
533
+ elapsed() { return time > this.time; }
506
534
 
507
535
  /** Get how long since elapsed, returns 0 if not set (returns negative if currently active)
508
536
  * @return {Number} */
@@ -511,6 +539,10 @@ class Timer
511
539
  /** Get percentage elapsed based on time it was set to, returns 0 if not set
512
540
  * @return {Number} */
513
541
  getPercent() { return this.isSet()? percent(this.time - time, this.setTime, 0) : 0; }
542
+
543
+ /** Returns this timer expressed as a string
544
+ * @return {String} */
545
+ toString() { return this.unset() ? 'unset' : Math.abs(this.get()) + ' seconds ' + (this.get()<0 ? 'before' : 'after' ); }
514
546
  }
515
547
  /**
516
548
  * LittleJS Engine Settings
@@ -753,7 +785,7 @@ let medalDisplayIconSize = 50;
753
785
  const engineName = 'LittleJS';
754
786
 
755
787
  /** Version of engine */
756
- const engineVersion = '1.2.3';
788
+ const engineVersion = '1.2.8';
757
789
 
758
790
  /** Frames per second to update objects
759
791
  * @default */
@@ -834,8 +866,6 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
834
866
  // main update loop
835
867
  const engineUpdate = (frameTimeMS=0)=>
836
868
  {
837
- requestAnimationFrame(engineUpdate);
838
-
839
869
  // update time keeping
840
870
  let frameTimeDeltaMS = frameTimeMS - frameTimeLastMS;
841
871
  frameTimeLastMS = frameTimeMS;
@@ -934,6 +964,8 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
934
964
  overlayContext.fillText(text, mainCanvas.width-2, 2);
935
965
  drawCount = 0;
936
966
  }
967
+
968
+ requestAnimationFrame(engineUpdate);
937
969
  }
938
970
 
939
971
  // set tile image source to load the image and start the engine
@@ -1051,7 +1083,7 @@ class EngineObject
1051
1083
  * @param {Vector2} [size=objectDefaultSize] - World space size of the object
1052
1084
  * @param {Number} [tileIndex=-1] - Tile to use to render object, untextured if -1
1053
1085
  * @param {Vector2} [tileSize=tileSizeDefault] - Size of tile in source pixels
1054
- * @param {Number} [angle=0] - Angle to rotate the object
1086
+ * @param {Number} [angle=0] - Angle the object is rotated by
1055
1087
  * @param {Color} [color] - Color to apply to tile when rendered
1056
1088
  * @param {Number} [renderOrder=0] - Objects sorted by renderOrder before being rendered
1057
1089
  */
@@ -1367,6 +1399,22 @@ class EngineObject
1367
1399
  this.isSolid = isSolid;
1368
1400
  this.collideTiles = collideTiles;
1369
1401
  }
1402
+
1403
+ toString()
1404
+ {
1405
+ let text = 'type = ' + this.constructor.name;
1406
+ if (this.pos.x || this.pos.y)
1407
+ text += '\npos = ' + this.pos;
1408
+ if (this.velocity.x || this.velocity.y)
1409
+ text += '\nvelocity = ' + this.velocity;
1410
+ if (this.size.x || this.size.y)
1411
+ text += '\nsize = ' + this.size;
1412
+ if (this.angle)
1413
+ text += '\nangle = ' + this.angle.toFixed(3);
1414
+ if (this.color)
1415
+ text += '\ncolor = ' + this.color;
1416
+ return text;
1417
+ }
1370
1418
  }
1371
1419
  /**
1372
1420
  * LittleJS Drawing System
@@ -1480,7 +1528,7 @@ function drawTile(pos, size=vec2(1), tileIndex=-1, tileSize=tileSizeDefault, col
1480
1528
  if (tileIndex < 0)
1481
1529
  {
1482
1530
  // if negative tile index, force untextured
1483
- context.fillStyle = color.rgba();
1531
+ context.fillStyle = color;
1484
1532
  context.fillRect(-.5, -.5, 1, 1);
1485
1533
  }
1486
1534
  else
@@ -1585,7 +1633,8 @@ function setBlendMode(additive, useWebGL=glEnable)
1585
1633
  mainContext.globalCompositeOperation = additive ? 'lighter' : 'source-over';
1586
1634
  }
1587
1635
 
1588
- /** Draw text on overlay canvas in world space
1636
+ /** Draw text on overlay canvas in screen space
1637
+ * Automatically splits new lines into rows
1589
1638
  * @param {String} text
1590
1639
  * @param {Vector2} pos
1591
1640
  * @param {Number} [size=1]
@@ -1594,20 +1643,37 @@ function setBlendMode(additive, useWebGL=glEnable)
1594
1643
  * @param {Color} [lineColor=new Color(0,0,0)]
1595
1644
  * @param {String} [textAlign='center']
1596
1645
  * @memberof Draw */
1597
- function drawText(text, pos, size=1, color=new Color, lineWidth=0, lineColor=new Color(0,0,0), textAlign='center', font=fontDefault)
1646
+ function drawTextScreen(text, pos, size=1, color=new Color, lineWidth=0, lineColor=new Color(0,0,0), textAlign='center', font=fontDefault)
1598
1647
  {
1599
- pos = worldToScreen(pos);
1600
- overlayContext.font = size*cameraScale + 'px '+ font;
1648
+ overlayContext.fillStyle = color;
1649
+ overlayContext.lineWidth = lineWidth *= cameraScale;
1650
+ overlayContext.strokeStyle = lineColor;
1601
1651
  overlayContext.textAlign = textAlign;
1652
+ overlayContext.font = size + 'px '+ font;
1602
1653
  overlayContext.textBaseline = 'middle';
1603
- if (lineWidth)
1654
+
1655
+ pos = pos.copy();
1656
+ text.split('\n').forEach(line=>
1604
1657
  {
1605
- overlayContext.lineWidth = lineWidth*cameraScale;
1606
- overlayContext.strokeStyle = lineColor.rgba();
1607
- overlayContext.strokeText(text, pos.x, pos.y);
1608
- }
1609
- overlayContext.fillStyle = color.rgba();
1610
- overlayContext.fillText(text, pos.x, pos.y);
1658
+ lineWidth && overlayContext.strokeText(line, pos.x, pos.y);
1659
+ overlayContext.fillText(line, pos.x, pos.y);
1660
+ pos.y += size;
1661
+ });
1662
+ }
1663
+
1664
+ /** Draw text on overlay canvas in world space
1665
+ * Automatically splits new lines into rows
1666
+ * @param {String} text
1667
+ * @param {Vector2} pos
1668
+ * @param {Number} [size=1]
1669
+ * @param {Color} [color=new Color(1,1,1)]
1670
+ * @param {Number} [lineWidth=0]
1671
+ * @param {Color} [lineColor=new Color(0,0,0)]
1672
+ * @param {String} [textAlign='center']
1673
+ * @memberof Draw */
1674
+ function drawText(text, pos, size=1, color, lineWidth, lineColor, textAlign, font)
1675
+ {
1676
+ drawTextScreen(text, worldToScreen(pos), size*cameraScale, color, lineWidth, lineColor, textAlign, font);
1611
1677
  }
1612
1678
 
1613
1679
  ///////////////////////////////////////////////////////////////////////////////
@@ -2762,14 +2828,14 @@ class TileLayerData
2762
2828
  */
2763
2829
  class TileLayer extends EngineObject
2764
2830
  {
2765
- /** Create a tile layer object
2766
- * @param {Vector2} [position=new Vector2()] - World space position
2767
- * @param {Vector2} [size=objectDefaultSize] - World space size
2768
- * @param {Vector2} [tileSize=tileSizeDefault] - Size of tiles in source pixels
2769
- * @param {Vector2} [scale=new Vector2(1,1)] - How much to scale this layer when rendered
2770
- * @param {Number} [renderOrder=0] - Objects sorted by renderOrder before being rendered
2771
- */
2772
- constructor(pos, size=tileCollisionSize, tileSize=tileSizeDefault, scale=vec2(1), renderOrder=0)
2831
+ /** Create a tile layer object
2832
+ * @param {Vector2} [position=new Vector2()] - World space position
2833
+ * @param {Vector2} [size=tileCollisionSize] - World space size
2834
+ * @param {Vector2} [tileSize=tileSizeDefault] - Size of tiles in source pixels
2835
+ * @param {Vector2} [scale=new Vector2(1,1)] - How much to scale this layer when rendered
2836
+ * @param {Number} [renderOrder=0] - Objects sorted by renderOrder before being rendered
2837
+ */
2838
+ constructor(pos, size=tileCollisionSize, tileSize=tileSizeDefault, scale=vec2(1), renderOrder=0)
2773
2839
  {
2774
2840
  super(pos, size, -1, tileSize, 0, undefined, renderOrder);
2775
2841
 
@@ -2929,7 +2995,7 @@ class TileLayer extends EngineObject
2929
2995
  if (tileIndex < 0)
2930
2996
  {
2931
2997
  // untextured
2932
- context.fillStyle = color.rgba();
2998
+ context.fillStyle = color;
2933
2999
  context.fillRect(-.5, -.5, 1, 1);
2934
3000
  }
2935
3001
  else
@@ -2948,7 +3014,8 @@ class TileLayer extends EngineObject
2948
3014
  * @param {Vector2} [size=new Vector2(1,1)]
2949
3015
  * @param {Color} [color=new Color(1,1,1)]
2950
3016
  * @param {Number} [angle=0] */
2951
- drawRect(pos, size, color, angle) { this.drawTile(pos, size, -1, 0, color, angle); }
3017
+ drawRect(pos, size, color, angle)
3018
+ { this.drawTile(pos, size, -1, 0, color, angle); }
2952
3019
  }
2953
3020
  /*
2954
3021
  LittleJS Particle System
@@ -2980,7 +3047,7 @@ class ParticleEmitter extends EngineObject
2980
3047
  {
2981
3048
  /** Create a particle system with the given settings
2982
3049
  * @param {Vector2} position - World space position of the emitter
2983
- * @param {Number} [angle=0] - Angle to rotate the object
3050
+ * @param {Number} [angle=0] - Angle to emit the particles
2984
3051
  * @param {Number} [emitSize=0] - World space size of the emitter (float for circle diameter, vec2 for rect)
2985
3052
  * @param {Number} [emitTime=0] - How long to stay alive (0 is forever)
2986
3053
  * @param {Number} [emitRate=100] - How many particles per second to spawn, does not emit if 0
@@ -3271,7 +3338,7 @@ function medalsInit(saveName)
3271
3338
  {
3272
3339
  // check if medals are unlocked
3273
3340
  medalsSaveName = saveName;
3274
- debugMedals || medals.forEach(medal=> medal.unlocked = 1 || localStorage[medal.storageKey()]);
3341
+ debugMedals || medals.forEach(medal=> localStorage[medal.storageKey()]);
3275
3342
  }
3276
3343
 
3277
3344
  /**
@@ -3460,6 +3527,9 @@ class Newgrounds
3460
3527
  const scoreboardResult = this.call('ScoreBoard.getBoards');
3461
3528
  this.scoreboards = scoreboardResult ? scoreboardResult.result.data.scoreboards : [];
3462
3529
  debugMedals && console.log(this.scoreboards);
3530
+
3531
+ const keepAliveMS = 5 * 60 * 1e3;
3532
+ setInterval(()=>this.call('Gateway.ping', 0, 1), keepAliveMS);
3463
3533
  }
3464
3534
 
3465
3535
  /** Send message to unlock a medal by id
@@ -3581,7 +3651,7 @@ function glInit()
3581
3651
 
3582
3652
  // setup vertex and fragment shaders
3583
3653
  glShader = glCreateProgram(
3584
- 'precision highp float;'+ // use highp for better accuracy, lowp for better perf
3654
+ 'precision highp float;'+ // use highp for better accuracy
3585
3655
  'uniform mat4 m;'+ // transform matrix
3586
3656
  'attribute vec2 p,t;'+ // position, uv
3587
3657
  'attribute vec4 c,a;'+ // color, additiveColor
@@ -3592,7 +3662,7 @@ function glInit()
3592
3662
  'v=t;d=c;e=a;'+ // pass stuff to fragment shader
3593
3663
  '}' // end of shader
3594
3664
  ,
3595
- 'precision highp float;'+ // use highp for better accuracy, lowp for better perf
3665
+ 'precision highp float;'+ // use highp for better accuracy
3596
3666
  'varying vec2 v;'+ // uv
3597
3667
  'varying vec4 d,e;'+ // color, additiveColor
3598
3668
  'uniform sampler2D s;'+ // texture
package/engine/engine.js CHANGED
@@ -22,7 +22,7 @@
22
22
  const engineName = 'LittleJS';
23
23
 
24
24
  /** Version of engine */
25
- const engineVersion = '1.2.3';
25
+ const engineVersion = '1.2.9';
26
26
 
27
27
  /** Frames per second to update objects
28
28
  * @default */
@@ -103,8 +103,6 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
103
103
  // main update loop
104
104
  const engineUpdate = (frameTimeMS=0)=>
105
105
  {
106
- requestAnimationFrame(engineUpdate);
107
-
108
106
  // update time keeping
109
107
  let frameTimeDeltaMS = frameTimeMS - frameTimeLastMS;
110
108
  frameTimeLastMS = frameTimeMS;
@@ -203,6 +201,8 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
203
201
  overlayContext.fillText(text, mainCanvas.width-2, 2);
204
202
  drawCount = 0;
205
203
  }
204
+
205
+ requestAnimationFrame(engineUpdate);
206
206
  }
207
207
 
208
208
  // set tile image source to load the image and start the engine
@@ -273,17 +273,11 @@ const debugRender = ()=>
273
273
  drawRect(mousePos.floor().add(vec2(.5)), vec2(1), new Color(0,0,1,.5));
274
274
  drawLine(mousePos, bestObject.pos, .1, !raycastHitPos ? new Color(0,1,0,.5) : new Color(1,0,0,.5));
275
275
 
276
- let pos = mousePos.copy(), height = vec2(0,.5);
277
- const printVec2 = (v)=> '(' + (v.x>0?' ':'') + (v.x).toFixed(2) + ',' + (v.y>0?' ':'') + (v.y).toFixed(2) + ')';
278
- const args = [.5, new Color, .05, undefined, undefined, 'monospace'];
279
-
280
- drawText('pos = ' + printVec2(bestObject.pos)
281
- + (bestObject.angle>0?' ':' ') + (bestObject.angle*180/PI).toFixed(1) + '°',
282
- pos = pos.add(height), ...args);
283
- drawText('vel = ' + printVec2(bestObject.velocity), pos = pos.add(height), ...args);
284
- drawText('size = ' + printVec2(bestObject.size), pos = pos.add(height), ...args);
285
- drawText('type = ' + ( bestObject.constructor.name), pos = mousePos.subtract(height), ...args);
286
- drawText('collision = ' + getTileCollisionData(mousePos), pos = mousePos.subtract(height.scale(2)), ...args);
276
+ const debugText = 'mouse pos = ' + mousePos +
277
+ '\nmouse collision = ' + getTileCollisionData(mousePos) +
278
+ '\n\n--- object info ---\n' +
279
+ bestObject.toString();
280
+ drawTextScreen(debugText, mousePosScreen, 24, new Color, .05, undefined, undefined, 'monospace');
287
281
  mainContext = saveContext;
288
282
  }
289
283
 
@@ -292,11 +286,12 @@ const debugRender = ()=>
292
286
 
293
287
  {
294
288
  // draw debug primitives
295
- overlayContext.save();
296
289
  overlayContext.lineWidth = 2;
297
290
  const pointSize = debugPointSize * cameraScale;
298
291
  debugPrimitives.forEach(p=>
299
292
  {
293
+ overlayContext.save();
294
+
300
295
  // create canvas transform from world space to screen space
301
296
  const pos = worldToScreen(p.pos);
302
297
 
@@ -332,12 +327,12 @@ const debugRender = ()=>
332
327
  p.fill && overlayContext.fill();
333
328
  overlayContext.stroke();
334
329
  }
330
+
331
+ overlayContext.restore();
335
332
  });
336
333
 
337
334
  // remove expired pritives
338
335
  debugPrimitives = debugPrimitives.filter(r=>r.time.get()<0);
339
-
340
- overlayContext.restore();
341
336
  }
342
337
 
343
338
  {
@@ -110,7 +110,7 @@ function drawTile(pos, size=vec2(1), tileIndex=-1, tileSize=tileSizeDefault, col
110
110
  if (tileIndex < 0)
111
111
  {
112
112
  // if negative tile index, force untextured
113
- context.fillStyle = color.rgba();
113
+ context.fillStyle = color;
114
114
  context.fillRect(-.5, -.5, 1, 1);
115
115
  }
116
116
  else
@@ -215,7 +215,8 @@ function setBlendMode(additive, useWebGL=glEnable)
215
215
  mainContext.globalCompositeOperation = additive ? 'lighter' : 'source-over';
216
216
  }
217
217
 
218
- /** Draw text on overlay canvas in world space
218
+ /** Draw text on overlay canvas in screen space
219
+ * Automatically splits new lines into rows
219
220
  * @param {String} text
220
221
  * @param {Vector2} pos
221
222
  * @param {Number} [size=1]
@@ -224,20 +225,37 @@ function setBlendMode(additive, useWebGL=glEnable)
224
225
  * @param {Color} [lineColor=new Color(0,0,0)]
225
226
  * @param {String} [textAlign='center']
226
227
  * @memberof Draw */
227
- function drawText(text, pos, size=1, color=new Color, lineWidth=0, lineColor=new Color(0,0,0), textAlign='center', font=fontDefault)
228
+ function drawTextScreen(text, pos, size=1, color=new Color, lineWidth=0, lineColor=new Color(0,0,0), textAlign='center', font=fontDefault)
228
229
  {
229
- pos = worldToScreen(pos);
230
- overlayContext.font = size*cameraScale + 'px '+ font;
230
+ overlayContext.fillStyle = color;
231
+ overlayContext.lineWidth = lineWidth *= cameraScale;
232
+ overlayContext.strokeStyle = lineColor;
231
233
  overlayContext.textAlign = textAlign;
234
+ overlayContext.font = size + 'px '+ font;
232
235
  overlayContext.textBaseline = 'middle';
233
- if (lineWidth)
236
+
237
+ pos = pos.copy();
238
+ text.split('\n').forEach(line=>
234
239
  {
235
- overlayContext.lineWidth = lineWidth*cameraScale;
236
- overlayContext.strokeStyle = lineColor.rgba();
237
- overlayContext.strokeText(text, pos.x, pos.y);
238
- }
239
- overlayContext.fillStyle = color.rgba();
240
- overlayContext.fillText(text, pos.x, pos.y);
240
+ lineWidth && overlayContext.strokeText(line, pos.x, pos.y);
241
+ overlayContext.fillText(line, pos.x, pos.y);
242
+ pos.y += size;
243
+ });
244
+ }
245
+
246
+ /** Draw text on overlay canvas in world space
247
+ * Automatically splits new lines into rows
248
+ * @param {String} text
249
+ * @param {Vector2} pos
250
+ * @param {Number} [size=1]
251
+ * @param {Color} [color=new Color(1,1,1)]
252
+ * @param {Number} [lineWidth=0]
253
+ * @param {Color} [lineColor=new Color(0,0,0)]
254
+ * @param {String} [textAlign='center']
255
+ * @memberof Draw */
256
+ function drawText(text, pos, size=1, color, lineWidth, lineColor, textAlign, font)
257
+ {
258
+ drawTextScreen(text, worldToScreen(pos), size*cameraScale, color, lineWidth, lineColor, textAlign, font);
241
259
  }
242
260
 
243
261
  ///////////////////////////////////////////////////////////////////////////////
@@ -35,7 +35,7 @@ function medalsInit(saveName)
35
35
  {
36
36
  // check if medals are unlocked
37
37
  medalsSaveName = saveName;
38
- debugMedals || medals.forEach(medal=> medal.unlocked = 1 || localStorage[medal.storageKey()]);
38
+ debugMedals || medals.forEach(medal=> localStorage[medal.storageKey()]);
39
39
  }
40
40
 
41
41
  /**
@@ -224,6 +224,9 @@ class Newgrounds
224
224
  const scoreboardResult = this.call('ScoreBoard.getBoards');
225
225
  this.scoreboards = scoreboardResult ? scoreboardResult.result.data.scoreboards : [];
226
226
  debugMedals && console.log(this.scoreboards);
227
+
228
+ const keepAliveMS = 5 * 60 * 1e3;
229
+ setInterval(()=>this.call('Gateway.ping', 0, 1), keepAliveMS);
227
230
  }
228
231
 
229
232
  /** Send message to unlock a medal by id
@@ -36,7 +36,7 @@ class EngineObject
36
36
  * @param {Vector2} [size=objectDefaultSize] - World space size of the object
37
37
  * @param {Number} [tileIndex=-1] - Tile to use to render object, untextured if -1
38
38
  * @param {Vector2} [tileSize=tileSizeDefault] - Size of tile in source pixels
39
- * @param {Number} [angle=0] - Angle to rotate the object
39
+ * @param {Number} [angle=0] - Angle the object is rotated by
40
40
  * @param {Color} [color] - Color to apply to tile when rendered
41
41
  * @param {Number} [renderOrder=0] - Objects sorted by renderOrder before being rendered
42
42
  */
@@ -352,4 +352,20 @@ class EngineObject
352
352
  this.isSolid = isSolid;
353
353
  this.collideTiles = collideTiles;
354
354
  }
355
+
356
+ toString()
357
+ {
358
+ let text = 'type = ' + this.constructor.name;
359
+ if (this.pos.x || this.pos.y)
360
+ text += '\npos = ' + this.pos;
361
+ if (this.velocity.x || this.velocity.y)
362
+ text += '\nvelocity = ' + this.velocity;
363
+ if (this.size.x || this.size.y)
364
+ text += '\nsize = ' + this.size;
365
+ if (this.angle)
366
+ text += '\nangle = ' + this.angle.toFixed(3);
367
+ if (this.color)
368
+ text += '\ncolor = ' + this.color;
369
+ return text;
370
+ }
355
371
  }
@@ -28,7 +28,7 @@ class ParticleEmitter extends EngineObject
28
28
  {
29
29
  /** Create a particle system with the given settings
30
30
  * @param {Vector2} position - World space position of the emitter
31
- * @param {Number} [angle=0] - Angle to rotate the object
31
+ * @param {Number} [angle=0] - Angle to emit the particles
32
32
  * @param {Number} [emitSize=0] - World space size of the emitter (float for circle diameter, vec2 for rect)
33
33
  * @param {Number} [emitTime=0] - How long to stay alive (0 is forever)
34
34
  * @param {Number} [emitRate=100] - How many particles per second to spawn, does not emit if 0
@@ -153,14 +153,14 @@ class TileLayerData
153
153
  */
154
154
  class TileLayer extends EngineObject
155
155
  {
156
- /** Create a tile layer object
157
- * @param {Vector2} [position=new Vector2()] - World space position
158
- * @param {Vector2} [size=objectDefaultSize] - World space size
159
- * @param {Vector2} [tileSize=tileSizeDefault] - Size of tiles in source pixels
160
- * @param {Vector2} [scale=new Vector2(1,1)] - How much to scale this layer when rendered
161
- * @param {Number} [renderOrder=0] - Objects sorted by renderOrder before being rendered
162
- */
163
- constructor(pos, size=tileCollisionSize, tileSize=tileSizeDefault, scale=vec2(1), renderOrder=0)
156
+ /** Create a tile layer object
157
+ * @param {Vector2} [position=new Vector2()] - World space position
158
+ * @param {Vector2} [size=tileCollisionSize] - World space size
159
+ * @param {Vector2} [tileSize=tileSizeDefault] - Size of tiles in source pixels
160
+ * @param {Vector2} [scale=new Vector2(1,1)] - How much to scale this layer when rendered
161
+ * @param {Number} [renderOrder=0] - Objects sorted by renderOrder before being rendered
162
+ */
163
+ constructor(pos, size=tileCollisionSize, tileSize=tileSizeDefault, scale=vec2(1), renderOrder=0)
164
164
  {
165
165
  super(pos, size, -1, tileSize, 0, undefined, renderOrder);
166
166
 
@@ -320,7 +320,7 @@ class TileLayer extends EngineObject
320
320
  if (tileIndex < 0)
321
321
  {
322
322
  // untextured
323
- context.fillStyle = color.rgba();
323
+ context.fillStyle = color;
324
324
  context.fillRect(-.5, -.5, 1, 1);
325
325
  }
326
326
  else
@@ -339,5 +339,6 @@ class TileLayer extends EngineObject
339
339
  * @param {Vector2} [size=new Vector2(1,1)]
340
340
  * @param {Color} [color=new Color(1,1,1)]
341
341
  * @param {Number} [angle=0] */
342
- drawRect(pos, size, color, angle) { this.drawTile(pos, size, -1, 0, color, angle); }
342
+ drawRect(pos, size, color, angle)
343
+ { this.drawTile(pos, size, -1, 0, color, angle); }
343
344
  }
@@ -311,7 +311,13 @@ class Vector2
311
311
  /** Returns true if this vector is within the bounds of an array size passed in
312
312
  * @param {Vector2} arraySize
313
313
  * @return {Boolean} */
314
- arrayCheck(arraySize) { return this.x >= 0 && this.y >= 0 && this.x < arraySize.x && this.y < arraySize.y; }
314
+ arrayCheck(arraySize) { return this.x >= 0 && this.y >= 0 && this.x < arraySize.x && this.y < arraySize.y; }
315
+
316
+ /** Returns this vector expressed as a string
317
+ * @param {float} digits - precision to display
318
+ * @return {String} */
319
+ toString(digits=3)
320
+ { return `(${(this.x<0?'':' ') + this.x.toFixed(digits)},${(this.y<0?'':' ') + this.y.toFixed(digits)} )`; }
315
321
  }
316
322
 
317
323
  ///////////////////////////////////////////////////////////////////////////////
@@ -382,7 +388,7 @@ class Color
382
388
  * @return {Color} */
383
389
  lerp(c, p) { return this.add(c.subtract(this).scale(clamp(p))); }
384
390
 
385
- /** Sets this color given a hue, saturation, lightness , and alpha
391
+ /** Sets this color given a hue, saturation, lightness, and alpha
386
392
  * @param {Number} [hue=0]
387
393
  * @param {Number} [saturation=0]
388
394
  * @param {Number} [lightness=1]
@@ -418,21 +424,43 @@ class Color
418
424
  ).clamp();
419
425
  }
420
426
 
421
- /** Returns this color expressed as an rgba string
427
+ /** Returns this color expressed as an CSS color value
422
428
  * @return {String} */
423
- rgba()
429
+ toString()
424
430
  {
425
431
  ASSERT(this.r>=0 && this.r<=1 && this.g>=0 && this.g<=1 && this.b>=0 && this.b<=1 && this.a>=0 && this.a<=1);
426
432
  return `rgb(${this.r*255|0},${this.g*255|0},${this.b*255|0},${this.a})`;
427
433
  }
428
434
 
429
- /** Returns this color expressed as 32 bit integer value
435
+ /** Returns this color expressed as 32 bit integer RGBA value
430
436
  * @return {Number} */
431
437
  rgbaInt()
432
438
  {
433
439
  ASSERT(this.r>=0 && this.r<=1 && this.g>=0 && this.g<=1 && this.b>=0 && this.b<=1 && this.a>=0 && this.a<=1);
434
440
  return (this.r*255|0) + (this.g*255<<8) + (this.b*255<<16) + (this.a*255<<24);
435
441
  }
442
+
443
+ /** Returns this color expressed as a hex code
444
+ * @return {String} */
445
+ hex()
446
+ {
447
+ const toHex = (c)=> ((c=c*255|0)<16 ? '0' : '') + c.toString(16);
448
+ return '#' + toHex(this.r) + toHex(this.g) + toHex(this.b);
449
+ }
450
+
451
+ /** Set this color from a hex code
452
+ * @param {String} hex - html hex code
453
+ * @return {Color} */
454
+ setHex(hex)
455
+ {
456
+ const fromHex = (a)=> parseInt(hex.slice(a,a+2), 16)/255;
457
+ this.r = fromHex(1);
458
+ this.g = fromHex(3),
459
+ this.b = fromHex(5);
460
+ this.a = 1;
461
+ ASSERT(this.r>=0 && this.r<=1 && this.g>=0 && this.g<=1 && this.b>=0 && this.b<=1);
462
+ return this;
463
+ }
436
464
  }
437
465
 
438
466
  ///////////////////////////////////////////////////////////////////////////////
@@ -469,7 +497,7 @@ class Timer
469
497
 
470
498
  /** Returns true if set and elapsed
471
499
  * @return {Boolean} */
472
- elapsed() { return time > this.time; }
500
+ elapsed() { return time > this.time; }
473
501
 
474
502
  /** Get how long since elapsed, returns 0 if not set (returns negative if currently active)
475
503
  * @return {Number} */
@@ -478,4 +506,8 @@ class Timer
478
506
  /** Get percentage elapsed based on time it was set to, returns 0 if not set
479
507
  * @return {Number} */
480
508
  getPercent() { return this.isSet()? percent(this.time - time, this.setTime, 0) : 0; }
509
+
510
+ /** Returns this timer expressed as a string
511
+ * @return {String} */
512
+ toString() { return this.unset() ? 'unset' : Math.abs(this.get()) + ' seconds ' + (this.get()<0 ? 'before' : 'after' ); }
481
513
  }
@@ -47,7 +47,7 @@ function glInit()
47
47
 
48
48
  // setup vertex and fragment shaders
49
49
  glShader = glCreateProgram(
50
- 'precision highp float;'+ // use highp for better accuracy, lowp for better perf
50
+ 'precision highp float;'+ // use highp for better accuracy
51
51
  'uniform mat4 m;'+ // transform matrix
52
52
  'attribute vec2 p,t;'+ // position, uv
53
53
  'attribute vec4 c,a;'+ // color, additiveColor
@@ -58,7 +58,7 @@ function glInit()
58
58
  'v=t;d=c;e=a;'+ // pass stuff to fragment shader
59
59
  '}' // end of shader
60
60
  ,
61
- 'precision highp float;'+ // use highp for better accuracy, lowp for better perf
61
+ 'precision highp float;'+ // use highp for better accuracy
62
62
  'varying vec2 v;'+ // uv
63
63
  'varying vec4 d,e;'+ // color, additiveColor
64
64
  'uniform sampler2D s;'+ // texture