littlejsengine 1.9.0 → 1.9.2

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 (63) hide show
  1. package/README.md +7 -7
  2. package/{build → dist}/littlejs.d.ts +84 -77
  3. package/{build → dist}/littlejs.esm.js +245 -143
  4. package/dist/littlejs.esm.min.js +1 -0
  5. package/{build → dist}/littlejs.js +245 -143
  6. package/dist/littlejs.min.js +1 -0
  7. package/{build → dist}/littlejs.release.js +239 -141
  8. package/examples/breakout/game.js +2 -2
  9. package/examples/breakout/gameObjects.js +3 -3
  10. package/examples/breakout/index.html +3 -3
  11. package/examples/breakoutTutorial/index.html +2 -2
  12. package/examples/electron/build.js +1 -1
  13. package/examples/electron/game.js +49 -38
  14. package/examples/electron/index.html +2 -2
  15. package/examples/electron/tiles.png +0 -0
  16. package/examples/empty/index.html +1 -1
  17. package/examples/js13k/build.js +2 -2
  18. package/examples/js13k/game.js +10 -9
  19. package/examples/js13k/index.html +13 -13
  20. package/examples/js13k/tiles.png +0 -0
  21. package/examples/module/game.js +45 -41
  22. package/examples/module/index.html +1 -1
  23. package/examples/module/tiles.png +0 -0
  24. package/examples/particles/index.html +1 -1
  25. package/examples/platformer/data/gameTileData.tmx +143 -0
  26. package/examples/platformer/data/gameTileData.tsx +4 -0
  27. package/examples/platformer/game.js +0 -1
  28. package/examples/platformer/gameCharacter.js +1 -1
  29. package/examples/platformer/gameEffects.js +42 -110
  30. package/examples/platformer/gameLevel.js +149 -183
  31. package/examples/platformer/gameObjects.js +61 -16
  32. package/examples/platformer/gamePlayer.js +3 -2
  33. package/examples/platformer/gameTileData.js +181 -0
  34. package/examples/platformer/index.html +8 -7
  35. package/examples/platformer/tiles.png +0 -0
  36. package/examples/platformer/tilesLevel.png +0 -0
  37. package/examples/puzzle/game.js +12 -12
  38. package/examples/puzzle/index.html +2 -2
  39. package/examples/starter/build.js +2 -2
  40. package/examples/starter/game.js +6 -6
  41. package/examples/starter/index.html +13 -13
  42. package/examples/starter/tiles.png +0 -0
  43. package/examples/stress/index.html +2 -2
  44. package/examples/typescript/build.bat +4 -1
  45. package/examples/typescript/game.js +34 -31
  46. package/examples/typescript/game.ts +40 -36
  47. package/examples/typescript/index.html +1 -1
  48. package/examples/typescript/tiles.png +0 -0
  49. package/package.json +3 -3
  50. package/src/engine.js +1 -1
  51. package/src/engineAudio.js +4 -10
  52. package/src/engineBuild.js +9 -2
  53. package/src/engineDebug.js +6 -2
  54. package/src/engineDraw.js +27 -27
  55. package/src/engineInput.js +7 -7
  56. package/src/engineMedals.js +2 -2
  57. package/src/engineObject.js +7 -7
  58. package/src/engineParticles.js +9 -9
  59. package/src/engineTileLayer.js +38 -33
  60. package/src/engineUtilities.js +136 -35
  61. package/src/engineWebGL.js +8 -10
  62. package/build/littlejs.esm.min.js +0 -1
  63. package/build/littlejs.min.js +0 -1
@@ -53,7 +53,7 @@ function getTileCollisionData(pos)
53
53
 
54
54
  /** Check if collision with another object should occur
55
55
  * @param {Vector2} pos
56
- * @param {Vector2} [size=Vector2(1,1)]
56
+ * @param {Vector2} [size=(1,1)]
57
57
  * @param {EngineObject} [object]
58
58
  * @return {Boolean}
59
59
  * @memberof TileCollision */
@@ -138,7 +138,7 @@ class TileLayerData
138
138
  * @param {Number} [direction] - Integer direction of tile, in 90 degree increments
139
139
  * @param {Boolean} [mirror] - If the tile should be mirrored along the x axis
140
140
  * @param {Color} [color] - Color of the tile */
141
- constructor(tile, direction=0, mirror=false, color=new Color())
141
+ constructor(tile, direction=0, mirror=false, color=new Color)
142
142
  {
143
143
  /** @property {Number} - The tile to use, untextured if undefined */
144
144
  this.tile = tile;
@@ -169,11 +169,11 @@ class TileLayerData
169
169
  class TileLayer extends EngineObject
170
170
  {
171
171
  /** Create a tile layer object
172
- * @param {Vector2} [position=Vector2()] - World space position
172
+ * @param {Vector2} [position=(0,0)] - World space position
173
173
  * @param {Vector2} [size=tileCollisionSize] - World space size
174
- * @param {TileInfo} [tileInfo] - Tile info for layer
175
- * @param {Vector2} [scale=Vector2(1,1)] - How much to scale this layer when rendered
176
- * @param {Number} [renderOrder] - Objects sorted by renderOrder before being rendered
174
+ * @param {TileInfo} [tileInfo] - Tile info for layer
175
+ * @param {Vector2} [scale=(1,1)] - How much to scale this layer when rendered
176
+ * @param {Number} [renderOrder] - Objects are sorted by renderOrder
177
177
  */
178
178
  constructor(position, size=tileCollisionSize, tileInfo=tile(), scale=vec2(1), renderOrder=0)
179
179
  {
@@ -234,16 +234,18 @@ class TileLayer extends EngineObject
234
234
  }
235
235
 
236
236
  /** Draw all the tile data to an offscreen canvas
237
- * - This may be slow in some browsers
238
- */
237
+ * - This may be slow in some browsers but only needs to be done once */
239
238
  redraw()
240
239
  {
241
240
  this.redrawStart(true);
242
- this.drawAllTileData();
241
+ for (let x = this.size.x; x--;)
242
+ for (let y = this.size.y; y--;)
243
+ this.drawTileData(vec2(x,y), false);
243
244
  this.redrawEnd();
244
245
  }
245
246
 
246
247
  /** Call to start the redraw process
248
+ * - This can be used to manually update small parts of the level
247
249
  * @param {Boolean} [clear] - Should it clear the canvas before drawing */
248
250
  redrawStart(clear=false)
249
251
  {
@@ -251,17 +253,19 @@ class TileLayer extends EngineObject
251
253
  /** @type {[HTMLCanvasElement, CanvasRenderingContext2D, Vector2, Vector2, number]} */
252
254
  this.savedRenderSettings = [mainCanvas, mainContext, mainCanvasSize, cameraPos, cameraScale];
253
255
 
254
- // hack: use normal rendering system to render the tiles
256
+ // use webgl rendering system to render the tiles if enabled
257
+ // this works by temporally taking control of the rendering system
255
258
  mainCanvas = this.canvas;
256
259
  mainContext = this.context;
260
+ mainCanvasSize = this.size.multiply(this.tileInfo.size);
257
261
  cameraPos = this.size.scale(.5);
258
262
  cameraScale = this.tileInfo.size.x;
259
263
 
260
264
  if (clear)
261
265
  {
262
266
  // clear and set size
263
- mainCanvas.width = this.size.x * this.tileInfo.size.x;
264
- mainCanvas.height = this.size.y * this.tileInfo.size.y;
267
+ mainCanvas.width = mainCanvasSize.x;
268
+ mainCanvas.height = mainCanvasSize.y;
265
269
  }
266
270
 
267
271
  // begin a new render for the tile canvas
@@ -279,32 +283,33 @@ class TileLayer extends EngineObject
279
283
  [mainCanvas, mainContext, mainCanvasSize, cameraPos, cameraScale] = this.savedRenderSettings;
280
284
  }
281
285
 
282
- /** Draw the tile at a given position
283
- * @param {Vector2} layerPos */
284
- drawTileData(layerPos)
286
+ /** Draw the tile at a given position in the tile grid
287
+ * This can be used to clear out tiles when they are destroyed
288
+ * Tiles can also be redrawn if isinde a redrawStart/End block
289
+ * @param {Vector2} layerPos
290
+ * @param {Boolean} [clear] - should the old tile be cleared out
291
+ */
292
+ drawTileData(layerPos, clear=true)
285
293
  {
286
- // first clear out where the tile was
287
- const pos = layerPos.floor().add(this.pos).add(vec2(.5));
288
- this.drawCanvas2D(pos, vec2(1), 0, false, (context)=>context.clearRect(-.5, -.5, 1, 1));
294
+ // clear out where the tile was, for full opaque tiles this can be skipped
295
+ const s = this.tileInfo.size;
296
+ if (clear)
297
+ {
298
+ const pos = layerPos.multiply(s);
299
+ this.context.clearRect(pos.x, this.canvas.height-pos.y, s.x, -s.y);
300
+ }
289
301
 
290
302
  // draw the tile if not undefined
291
303
  const d = this.getData(layerPos);
292
304
  if (d.tile != undefined)
293
305
  {
306
+ const pos = this.pos.add(layerPos).add(vec2(.5));
294
307
  ASSERT(mainContext == this.context, 'must call redrawStart() before drawing tiles');
295
- const tileInfo = tile(d.tile, this.tileInfo.size, this.tileInfo.textureIndex);
308
+ const tileInfo = tile(d.tile, s, this.tileInfo.textureIndex);
296
309
  drawTile(pos, vec2(1), tileInfo, d.color, d.direction*PI/2, d.mirror);
297
310
  }
298
311
  }
299
312
 
300
- /** Draw all the tiles in this layer */
301
- drawAllTileData()
302
- {
303
- for (let x = this.size.x; x--;)
304
- for (let y = this.size.y; y--;)
305
- this.drawTileData(vec2(x,y));
306
- }
307
-
308
313
  /** Draw directly to the 2D canvas in world space (bipass webgl)
309
314
  * @param {Vector2} pos
310
315
  * @param {Vector2} size
@@ -324,11 +329,11 @@ class TileLayer extends EngineObject
324
329
  context.restore();
325
330
  }
326
331
 
327
- /** Draw a tile directly onto the layer canvas
332
+ /** Draw a tile directly onto the layer canvas in world space
328
333
  * @param {Vector2} pos
329
- * @param {Vector2} [size=Vector2(1,1)]
334
+ * @param {Vector2} [size=(1,1)]
330
335
  * @param {TileInfo} [tileInfo]
331
- * @param {Color} [color=Color()]
336
+ * @param {Color} [color=(1,1,1,1)]
332
337
  * @param {Number} [angle=0]
333
338
  * @param {Boolean} [mirror=0] */
334
339
  drawTile(pos, size=vec2(1), tileInfo, color=new Color, angle, mirror)
@@ -353,10 +358,10 @@ class TileLayer extends EngineObject
353
358
  });
354
359
  }
355
360
 
356
- /** Draw a rectangle directly onto the layer canvas
361
+ /** Draw a rectangle directly onto the layer canvas in world space
357
362
  * @param {Vector2} pos
358
- * @param {Vector2} [size=Vector2(1,1)]
359
- * @param {Color} [color=Color()]
363
+ * @param {Vector2} [size=(1,1)]
364
+ * @param {Color} [color=(1,1,1,1)]
360
365
  * @param {Number} [angle=0] */
361
366
  drawRect(pos, size, color, angle)
362
367
  { this.drawTile(pos, size, undefined, color, angle); }
@@ -121,13 +121,13 @@ function smoothStep(percent) { return percent * percent * (3 - 2 * percent); }
121
121
  function nearestPowerOfTwo(value) { return 2**Math.ceil(Math.log2(value)); }
122
122
 
123
123
  /** Returns true if two axis aligned bounding boxes are overlapping
124
- * @param {Vector2} pointA - Center of box A
125
- * @param {Vector2} sizeA - Size of box A
126
- * @param {Vector2} pointB - Center of box B
127
- * @param {Vector2} sizeB - Size of box B
128
- * @return {Boolean} - True if overlapping
124
+ * @param {Vector2} pointA - Center of box A
125
+ * @param {Vector2} sizeA - Size of box A
126
+ * @param {Vector2} pointB - Center of box B
127
+ * @param {Vector2} [sizeB=(0,0)] - Size of box B, a point if undefined
128
+ * @return {Boolean} - True if overlapping
129
129
  * @memberof Utilities */
130
- function isOverlapping(pointA, sizeA, pointB, sizeB)
130
+ function isOverlapping(pointA, sizeA, pointB, sizeB=vec2())
131
131
  {
132
132
  return abs(pointA.x - pointB.x)*2 < sizeA.x + sizeB.x
133
133
  && abs(pointA.y - pointB.y)*2 < sizeA.y + sizeB.y;
@@ -187,8 +187,8 @@ function randInCircle(radius=1, minRadius=0)
187
187
  { return radius > 0 ? randVector(radius * rand(minRadius / radius, 1)**.5) : new Vector2; }
188
188
 
189
189
  /** Returns a random color between the two passed in colors, combine components if linear
190
- * @param {Color} [colorA=Color()]
191
- * @param {Color} [colorB=Color(0,0,0,1)]
190
+ * @param {Color} [colorA=(1,1,1,1)]
191
+ * @param {Color} [colorB=(0,0,0,1)]
192
192
  * @param {Boolean} [linear]
193
193
  * @return {Color}
194
194
  * @memberof Random */
@@ -259,7 +259,11 @@ class RandomGenerator
259
259
  * @memberof Utilities
260
260
  */
261
261
  function vec2(x=0, y)
262
- { return typeof x === 'number'? new Vector2(x, y == undefined? x : y) : new Vector2(x.x, x.y); }
262
+ {
263
+ return typeof x === 'number' ?
264
+ new Vector2(x, y == undefined? x : y) :
265
+ new Vector2(x.x, x.y);
266
+ }
263
267
 
264
268
  /**
265
269
  * Check if object is a valid Vector2
@@ -267,7 +271,7 @@ function vec2(x=0, y)
267
271
  * @return {Boolean}
268
272
  * @memberof Utilities
269
273
  */
270
- function isVector2(v) { return typeof v === 'object' && typeof v.x === 'number' && typeof v.y === 'number'; }
274
+ function isVector2(v) { return v instanceof Vector2; }
271
275
 
272
276
  /**
273
277
  * 2D Vector object with vector math library
@@ -298,27 +302,47 @@ class Vector2
298
302
  /** Returns a copy of this vector plus the vector passed in
299
303
  * @param {Vector2} v - other vector
300
304
  * @return {Vector2} */
301
- add(v) { ASSERT(isVector2(v)); return new Vector2(this.x + v.x, this.y + v.y); }
305
+ add(v)
306
+ {
307
+ ASSERT(isVector2(v));
308
+ return new Vector2(this.x + v.x, this.y + v.y);
309
+ }
302
310
 
303
311
  /** Returns a copy of this vector minus the vector passed in
304
312
  * @param {Vector2} v - other vector
305
313
  * @return {Vector2} */
306
- subtract(v) { ASSERT(isVector2(v)); return new Vector2(this.x - v.x, this.y - v.y); }
314
+ subtract(v)
315
+ {
316
+ ASSERT(isVector2(v));
317
+ return new Vector2(this.x - v.x, this.y - v.y);
318
+ }
307
319
 
308
320
  /** Returns a copy of this vector times the vector passed in
309
321
  * @param {Vector2} v - other vector
310
322
  * @return {Vector2} */
311
- multiply(v) { ASSERT(isVector2(v)); return new Vector2(this.x * v.x, this.y * v.y); }
323
+ multiply(v)
324
+ {
325
+ ASSERT(isVector2(v));
326
+ return new Vector2(this.x * v.x, this.y * v.y);
327
+ }
312
328
 
313
329
  /** Returns a copy of this vector divided by the vector passed in
314
330
  * @param {Vector2} v - other vector
315
331
  * @return {Vector2} */
316
- divide(v) { ASSERT(isVector2(v)); return new Vector2(this.x / v.x, this.y / v.y); }
332
+ divide(v)
333
+ {
334
+ ASSERT(isVector2(v));
335
+ return new Vector2(this.x / v.x, this.y / v.y);
336
+ }
317
337
 
318
338
  /** Returns a copy of this vector scaled by the vector passed in
319
339
  * @param {Number} s - scale
320
340
  * @return {Vector2} */
321
- scale(s) { ASSERT(!isVector2(s)); return new Vector2(this.x * s, this.y * s); }
341
+ scale(s)
342
+ {
343
+ ASSERT(!isVector2(s));
344
+ return new Vector2(this.x * s, this.y * s);
345
+ }
322
346
 
323
347
  /** Returns the length of this vector
324
348
  * @return {Number} */
@@ -331,32 +355,56 @@ class Vector2
331
355
  /** Returns the distance from this vector to vector passed in
332
356
  * @param {Vector2} v - other vector
333
357
  * @return {Number} */
334
- distance(v) { return this.distanceSquared(v)**.5; }
358
+ distance(v)
359
+ {
360
+ ASSERT(isVector2(v));
361
+ return this.distanceSquared(v)**.5;
362
+ }
335
363
 
336
364
  /** Returns the distance squared from this vector to vector passed in
337
365
  * @param {Vector2} v - other vector
338
366
  * @return {Number} */
339
- distanceSquared(v) { return (this.x - v.x)**2 + (this.y - v.y)**2; }
367
+ distanceSquared(v)
368
+ {
369
+ ASSERT(isVector2(v));
370
+ return (this.x - v.x)**2 + (this.y - v.y)**2;
371
+ }
340
372
 
341
373
  /** Returns a new vector in same direction as this one with the length passed in
342
374
  * @param {Number} [length]
343
375
  * @return {Vector2} */
344
- normalize(length=1) { const l = this.length(); return l ? this.scale(length/l) : new Vector2(0, length); }
376
+ normalize(length=1)
377
+ {
378
+ const l = this.length();
379
+ return l ? this.scale(length/l) : new Vector2(0, length);
380
+ }
345
381
 
346
382
  /** Returns a new vector clamped to length passed in
347
383
  * @param {Number} [length]
348
384
  * @return {Vector2} */
349
- clampLength(length=1) { const l = this.length(); return l > length ? this.scale(length/l) : this; }
385
+ clampLength(length=1)
386
+ {
387
+ const l = this.length();
388
+ return l > length ? this.scale(length/l) : this;
389
+ }
350
390
 
351
391
  /** Returns the dot product of this and the vector passed in
352
392
  * @param {Vector2} v - other vector
353
393
  * @return {Number} */
354
- dot(v) { ASSERT(isVector2(v)); return this.x*v.x + this.y*v.y; }
394
+ dot(v)
395
+ {
396
+ ASSERT(isVector2(v));
397
+ return this.x*v.x + this.y*v.y;
398
+ }
355
399
 
356
400
  /** Returns the cross product of this and the vector passed in
357
401
  * @param {Vector2} v - other vector
358
402
  * @return {Number} */
359
- cross(v) { ASSERT(isVector2(v)); return this.x*v.y - this.y*v.x; }
403
+ cross(v)
404
+ {
405
+ ASSERT(isVector2(v));
406
+ return this.x*v.y - this.y*v.x;
407
+ }
360
408
 
361
409
  /** Returns the angle of this vector, up is angle 0
362
410
  * @return {Number} */
@@ -367,7 +415,11 @@ class Vector2
367
415
  * @param {Number} [length]
368
416
  * @return {Vector2} */
369
417
  setAngle(angle=0, length=1)
370
- { this.x = length*Math.sin(angle); this.y = length*Math.cos(angle); return this; }
418
+ {
419
+ this.x = length*Math.sin(angle);
420
+ this.y = length*Math.cos(angle);
421
+ return this;
422
+ }
371
423
 
372
424
  /** Returns copy of this vector rotated by the angle passed in
373
425
  * @param {Number} angle
@@ -378,9 +430,20 @@ class Vector2
378
430
  return new Vector2(this.x*c - this.y*s, this.x*s + this.y*c);
379
431
  }
380
432
 
433
+ /** Set the integer direction of this vector, corrosponding to multiples of 90 degree rotation (0-3)
434
+ * @param {Number} [direction]
435
+ * @param {Number} [length] */
436
+ setDirection(direction, length=1)
437
+ {
438
+ ASSERT(direction==0 || direction==1 || direction==2 || direction==3);
439
+ return vec2(direction%2 ? direction-1 ? -length : length : 0,
440
+ direction%2 ? 0 : direction ? -length : length);
441
+ }
442
+
381
443
  /** Returns the integer direction of this vector, corrosponding to multiples of 90 degree rotation (0-3)
382
444
  * @return {Number} */
383
- direction() { return abs(this.x) > abs(this.y) ? this.x < 0 ? 3 : 1 : this.y < 0 ? 2 : 0; }
445
+ direction()
446
+ { return abs(this.x) > abs(this.y) ? this.x < 0 ? 3 : 1 : this.y < 0 ? 2 : 0; }
384
447
 
385
448
  /** Returns a copy of this vector that has been inverted
386
449
  * @return {Vector2} */
@@ -399,24 +462,34 @@ class Vector2
399
462
  * @param {Number} percent
400
463
  * @return {Vector2} */
401
464
  lerp(v, percent)
402
- { ASSERT(isVector2(v)); return this.add(v.subtract(this).scale(clamp(percent))); }
465
+ {
466
+ ASSERT(isVector2(v));
467
+ return this.add(v.subtract(this).scale(clamp(percent)));
468
+ }
403
469
 
404
470
  /** Returns true if this vector is within the bounds of an array size passed in
405
471
  * @param {Vector2} arraySize
406
472
  * @return {Boolean} */
407
- arrayCheck(arraySize) { return this.x >= 0 && this.y >= 0 && this.x < arraySize.x && this.y < arraySize.y; }
473
+ arrayCheck(arraySize)
474
+ {
475
+ ASSERT(isVector2(arraySize));
476
+ return this.x >= 0 && this.y >= 0 && this.x < arraySize.x && this.y < arraySize.y;
477
+ }
408
478
 
409
479
  /** Returns this vector expressed as a string
410
480
  * @param {Number} digits - precision to display
411
481
  * @return {String} */
412
482
  toString(digits=3)
413
- { if (debug) { return `(${(this.x<0?'':' ') + this.x.toFixed(digits)},${(this.y<0?'':' ') + this.y.toFixed(digits)} )`; }}
483
+ {
484
+ if (debug)
485
+ return `(${(this.x<0?'':' ') + this.x.toFixed(digits)},${(this.y<0?'':' ') + this.y.toFixed(digits)} )`;
486
+ }
414
487
  }
415
488
 
416
489
  ///////////////////////////////////////////////////////////////////////////////
417
490
 
418
491
  /**
419
- * Create a color object with RGBA values
492
+ * Create a color object with RGBA values, white by default
420
493
  * @param {Number} [r=1] - red
421
494
  * @param {Number} [g=1] - green
422
495
  * @param {Number} [b=1] - blue
@@ -427,7 +500,7 @@ class Vector2
427
500
  function rgb(r, g, b, a) { return new Color(r, g, b, a); }
428
501
 
429
502
  /**
430
- * Create a color object with HSLA values
503
+ * Create a color object with HSLA values, white by default
431
504
  * @param {Number} [h=0] - hue
432
505
  * @param {Number} [s=0] - saturation
433
506
  * @param {Number} [l=1] - lightness
@@ -437,14 +510,22 @@ function rgb(r, g, b, a) { return new Color(r, g, b, a); }
437
510
  */
438
511
  function hsl(h, s, l, a) { return new Color().setHSLA(h, s, l, a); }
439
512
 
513
+ /**
514
+ * Check if object is a valid Color
515
+ * @param {any} c
516
+ * @return {Boolean}
517
+ * @memberof Utilities
518
+ */
519
+ function isColor(c) { return c instanceof Color; }
520
+
440
521
  /**
441
522
  * Color object (red, green, blue, alpha) with some helpful functions
442
523
  * @example
443
524
  * let a = new Color; // white
444
525
  * let b = new Color(1, 0, 0); // red
445
526
  * let c = new Color(0, 0, 0, 0); // transparent black
446
- * let d = RGB(0, 0, 1); // blue using rgb color
447
- * let e = HSL(.3, 1, .5); // green using hsl color
527
+ * let d = rgb(0, 0, 1); // blue using rgb color
528
+ * let e = hsl(.3, 1, .5); // green using hsl color
448
529
  */
449
530
  class Color
450
531
  {
@@ -472,22 +553,38 @@ class Color
472
553
  /** Returns a copy of this color plus the color passed in
473
554
  * @param {Color} c - other color
474
555
  * @return {Color} */
475
- add(c) { return new Color(this.r+c.r, this.g+c.g, this.b+c.b, this.a+c.a); }
556
+ add(c)
557
+ {
558
+ ASSERT(isColor(c));
559
+ return new Color(this.r+c.r, this.g+c.g, this.b+c.b, this.a+c.a);
560
+ }
476
561
 
477
562
  /** Returns a copy of this color minus the color passed in
478
563
  * @param {Color} c - other color
479
564
  * @return {Color} */
480
- subtract(c) { return new Color(this.r-c.r, this.g-c.g, this.b-c.b, this.a-c.a); }
565
+ subtract(c)
566
+ {
567
+ ASSERT(isColor(c));
568
+ return new Color(this.r-c.r, this.g-c.g, this.b-c.b, this.a-c.a);
569
+ }
481
570
 
482
571
  /** Returns a copy of this color times the color passed in
483
572
  * @param {Color} c - other color
484
573
  * @return {Color} */
485
- multiply(c) { return new Color(this.r*c.r, this.g*c.g, this.b*c.b, this.a*c.a); }
574
+ multiply(c)
575
+ {
576
+ ASSERT(isColor(c));
577
+ return new Color(this.r*c.r, this.g*c.g, this.b*c.b, this.a*c.a);
578
+ }
486
579
 
487
580
  /** Returns a copy of this color divided by the color passed in
488
581
  * @param {Color} c - other color
489
582
  * @return {Color} */
490
- divide(c) { return new Color(this.r/c.r, this.g/c.g, this.b/c.b, this.a/c.a); }
583
+ divide(c)
584
+ {
585
+ ASSERT(isColor(c));
586
+ return new Color(this.r/c.r, this.g/c.g, this.b/c.b, this.a/c.a);
587
+ }
491
588
 
492
589
  /** Returns a copy of this color scaled by the value passed in, alpha can be scaled separately
493
590
  * @param {Number} scale
@@ -504,7 +601,11 @@ class Color
504
601
  * @param {Color} c - other color
505
602
  * @param {Number} percent
506
603
  * @return {Color} */
507
- lerp(c, percent) { return this.add(c.subtract(this).scale(clamp(percent))); }
604
+ lerp(c, percent)
605
+ {
606
+ ASSERT(isColor(c));
607
+ return this.add(c.subtract(this).scale(clamp(percent)));
608
+ }
508
609
 
509
610
  /** Sets this color given a hue, saturation, lightness, and alpha
510
611
  * @param {Number} [h] - hue
@@ -248,8 +248,10 @@ function glCopyToContext(context, forceDraw=false)
248
248
  * @memberof WebGL */
249
249
  function glDraw(x, y, sizeX, sizeY, angle, uv0X, uv0Y, uv1X, uv1Y, rgba, rgbaAdditive=0)
250
250
  {
251
+ ASSERT(typeof rgba == 'number' && typeof rgbaAdditive == 'number', 'invalid color');
252
+
251
253
  // flush if there is not enough room or if different blend mode
252
- if (glInstanceCount >= gl_MAX_INSTANCES-1 || glBatchAdditive != glAdditive)
254
+ if (glInstanceCount >= gl_MAX_INSTANCES || glBatchAdditive != glAdditive)
253
255
  glFlush();
254
256
 
255
257
  let offset = glInstanceCount * gl_INDICIES_PER_INSTANCE;
@@ -276,7 +278,7 @@ let glPostShader, glPostArrayBuffer, glPostTexture, glPostIncludeOverlay;
276
278
  * @param {String} shaderCode
277
279
  * @param {Boolean} includeOverlay
278
280
  * @memberof WebGL */
279
- function glInitPostProcess(shaderCode, includeOverlay)
281
+ function glInitPostProcess(shaderCode, includeOverlay=false)
280
282
  {
281
283
  ASSERT(!glPostShader, 'can only have 1 post effects shader');
282
284
 
@@ -312,6 +314,8 @@ function glInitPostProcess(shaderCode, includeOverlay)
312
314
 
313
315
  // hide the original 2d canvas
314
316
  mainCanvas.style.visibility = 'hidden';
317
+ if (glPostIncludeOverlay)
318
+ overlayCanvas.style.visibility = 'hidden';
315
319
  }
316
320
 
317
321
  // Render the post processing shader, called automatically by the engine
@@ -332,14 +336,8 @@ function glRenderPostProcess()
332
336
  glContext.viewport(0, 0, glCanvas.width = mainCanvas.width, glCanvas.height = mainCanvas.height);
333
337
  }
334
338
 
335
- if (glPostIncludeOverlay)
336
- {
337
- // copy overlay canvas so it will be included in post processing
338
- mainContext.drawImage(overlayCanvas, 0, 0);
339
-
340
- // clear overlay canvas
341
- overlayCanvas.width = mainCanvas.width;
342
- }
339
+ // copy overlay canvas so it will be included in post processing
340
+ glPostIncludeOverlay && mainContext.drawImage(overlayCanvas, 0, 0);
343
341
 
344
342
  // setup shader program to draw one triangle
345
343
  glContext.useProgram(glPostShader);