littlejsengine 1.9.1 → 1.9.3

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 (68) hide show
  1. package/README.md +11 -9
  2. package/{build → dist}/littlejs.d.ts +48 -43
  3. package/{build → dist}/littlejs.esm.js +256 -147
  4. package/dist/littlejs.esm.min.js +1 -0
  5. package/{build → dist}/littlejs.js +256 -147
  6. package/dist/littlejs.min.js +1 -0
  7. package/{build → dist}/littlejs.release.js +249 -144
  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 +1 -1
  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/logo.png +0 -0
  22. package/examples/module/game.js +45 -41
  23. package/examples/module/index.html +1 -1
  24. package/examples/module/tiles.png +0 -0
  25. package/examples/particles/index.html +1 -1
  26. package/examples/platformer/data/gameLevelData.tmx +143 -0
  27. package/examples/platformer/data/gameLevelData.tsx +4 -0
  28. package/examples/platformer/game.js +31 -11
  29. package/examples/platformer/gameCharacter.js +9 -7
  30. package/examples/platformer/gameEffects.js +105 -164
  31. package/examples/platformer/gameLevel.js +164 -181
  32. package/examples/platformer/gameLevelData.js +181 -0
  33. package/examples/platformer/gameObjects.js +71 -26
  34. package/examples/platformer/gamePlayer.js +3 -2
  35. package/examples/platformer/index.html +8 -7
  36. package/examples/platformer/tiles.png +0 -0
  37. package/examples/platformer/tilesLevel.png +0 -0
  38. package/examples/puzzle/game.js +12 -12
  39. package/examples/puzzle/index.html +2 -2
  40. package/examples/screenshot.jpg +0 -0
  41. package/examples/starter/build.js +1 -1
  42. package/examples/starter/game.js +6 -6
  43. package/examples/starter/index.html +13 -13
  44. package/examples/starter/tiles.png +0 -0
  45. package/examples/stress/index.html +2 -2
  46. package/examples/typescript/build.bat +4 -1
  47. package/examples/typescript/game.js +34 -31
  48. package/examples/typescript/game.ts +40 -36
  49. package/examples/typescript/index.html +1 -1
  50. package/examples/typescript/tiles.png +0 -0
  51. package/package.json +3 -3
  52. package/reference.md +409 -0
  53. package/src/engine.js +46 -41
  54. package/src/engineAudio.js +22 -19
  55. package/src/engineBuild.js +9 -2
  56. package/src/engineDebug.js +7 -3
  57. package/src/engineDraw.js +8 -7
  58. package/src/engineInput.js +5 -5
  59. package/src/engineMedals.js +3 -3
  60. package/src/engineObject.js +2 -2
  61. package/src/engineParticles.js +16 -15
  62. package/src/engineSettings.js +3 -3
  63. package/src/engineTileLayer.js +3 -3
  64. package/src/engineUtilities.js +139 -38
  65. package/src/engineWebGL.js +2 -8
  66. package/build/littlejs.esm.min.js +0 -1
  67. package/build/littlejs.min.js +0 -1
  68. package/index.d.ts +0 -2094
@@ -11,7 +11,7 @@
11
11
  'use strict';
12
12
 
13
13
  const ENGINE_NAME = 'littlejs';
14
- const BUILD_FOLDER = 'build';
14
+ const BUILD_FOLDER = 'dist';
15
15
  const SOURCE_FOLDER = 'src';
16
16
  const engineSourceFiles =
17
17
  [
@@ -158,7 +158,14 @@ function typeScriptBuildStep(filename)
158
158
  {
159
159
  try
160
160
  {
161
- child_process.execSync(`npx tsc ${filename} --declaration --allowJs --emitDeclarationOnly --outFile ${BUILD_FOLDER}/${ENGINE_NAME}.d.ts`);
161
+ const tsFilename = `${BUILD_FOLDER}/${ENGINE_NAME}.d.ts`
162
+ child_process.execSync(`npx tsc ${filename} --declaration --allowJs --emitDeclarationOnly --outFile ${tsFilename}`);
163
+
164
+ // Remove declare module part
165
+ //let fileContent = fs.readFileSync(tsFilename, 'utf8');
166
+ //const r = new RegExp(`declare module "${ENGINE_NAME}\.esm" \{([\\s\\S]*?)\}`);
167
+ //fs.writeFileSync(tsFilename, fileContent.replace(r, '$1'));
168
+
162
169
  }
163
170
  catch (e) { handleError(e, 'Failed to run TypeScript build step!'); }
164
171
  };
@@ -52,11 +52,15 @@ let debugPrimitives = [], debugPhysics = false, debugRaycast = false, debugParti
52
52
  ///////////////////////////////////////////////////////////////////////////////
53
53
  // Debug helper functions
54
54
 
55
- /** Asserts if the experssion is false, does not do anything in release builds
55
+ /** Asserts if the expression is false, does not do anything in release builds
56
56
  * @param {Boolean} assert
57
- * @param {Object} output
57
+ * @param {Object} [output]
58
58
  * @memberof Debug */
59
- function ASSERT(assert, output) { enableAsserts && console.assert(assert, output); }
59
+ function ASSERT(assert, output)
60
+ {
61
+ if (enableAsserts)
62
+ output ? console.assert(assert, output) : console.assert(assert);
63
+ }
60
64
 
61
65
  /** Draw a debug rectangle in world space
62
66
  * @param {Vector2} pos
package/src/engineDraw.js CHANGED
@@ -85,13 +85,9 @@ function tile(pos=vec2(), size=tileSizeDefault, textureIndex=0)
85
85
  if (typeof pos === 'number')
86
86
  {
87
87
  const textureInfo = textureInfos[textureIndex];
88
- if (textureInfo)
89
- {
90
- const cols = textureInfo.size.x / size.x |0;
91
- pos = vec2((pos%cols)*size.x, (pos/cols|0)*size.y);
92
- }
93
- else
94
- pos = vec2();
88
+ ASSERT(textureInfo, 'Texture not loaded');
89
+ const cols = textureInfo.size.x / size.x |0;
90
+ pos = vec2((pos%cols)*size.x, (pos/cols|0)*size.y);
95
91
  }
96
92
 
97
93
  // return a tile info object
@@ -180,6 +176,11 @@ function worldToScreen(worldPos)
180
176
  );
181
177
  }
182
178
 
179
+ /** Get the camera's visible area in world space
180
+ * @return {Vector2}
181
+ * @memberof Draw */
182
+ function getCameraSize() { return mainCanvasSize.scale(1/cameraScale); }
183
+
183
184
  /** Draw textured tile centered in world space, with color applied if using WebGL
184
185
  * @param {Vector2} pos - Center of the tile in world space
185
186
  * @param {Vector2} [size=(1,1)] - Size of the tile in world space
@@ -314,9 +314,9 @@ if (isTouchDevice)
314
314
  // handle all touch events the same way
315
315
  ontouchstart = ontouchmove = ontouchend = (e)=>
316
316
  {
317
- // fix stalled audio on mobile
318
- if (soundEnable)
319
- audioContext ? audioContext.resume() : zzfx(0);
317
+ // fix stalled audio requiring user interaction
318
+ if (soundEnable && audioContext && audioContext.state != 'running')
319
+ zzfx(0);
320
320
 
321
321
  // check if touching and pass to mouse events
322
322
  const touching = e.touches.length;
@@ -357,7 +357,7 @@ function createTouchGamepad()
357
357
  touchGamepadStick = vec2();
358
358
 
359
359
  const touchHandler = ontouchstart;
360
- ontouchstart = ontouchmove = ontouchend = (e)=>
360
+ ontouchstart = ontouchmove = ontouchend = (e)=>
361
361
  {
362
362
  // clear touch gamepad input
363
363
  touchGamepadStick = vec2();
@@ -462,7 +462,7 @@ function touchGamepadRender()
462
462
  const rightCenter = vec2(mainCanvasSize.x-touchGamepadSize, mainCanvasSize.y-touchGamepadSize);
463
463
  for (let i=4; i--;)
464
464
  {
465
- const pos = rightCenter.add(vec2().setAngle(i*PI/2, touchGamepadSize/2));
465
+ const pos = rightCenter.add(vec2().setDirection(i, touchGamepadSize/2));
466
466
  overlayContext.fillStyle = touchGamepadButtons[i] ? '#fff' : '#000';
467
467
  overlayContext.beginPath();
468
468
  overlayContext.arc(pos.x, pos.y, touchGamepadSize/4, 0,9);
@@ -90,8 +90,8 @@ class Medal
90
90
  // draw containing rect and clip to that region
91
91
  context.save();
92
92
  context.beginPath();
93
- context.fillStyle = rgb(.9,.9,.9).toString();
94
- context.strokeStyle = rgb(0,0,0).toString();
93
+ context.fillStyle = new Color(.9,.9,.9).toString();
94
+ context.strokeStyle = new Color(0,0,0).toString();
95
95
  context.lineWidth = 3;
96
96
  context.rect(x, y, width, medalDisplaySize.y);
97
97
  context.fill();
@@ -270,7 +270,7 @@ class Newgrounds
270
270
  }
271
271
 
272
272
  // build the input object
273
- const input =
273
+ const input =
274
274
  {
275
275
  'app_id': this.app_id,
276
276
  'session_id': this.session_id,
@@ -10,7 +10,7 @@
10
10
  * - Automatically adds self to object list
11
11
  * - Will be updated and rendered each frame
12
12
  * - Renders as a sprite from a tilesheet by default
13
- * - Can have color and addtive color applied
13
+ * - Can have color and additive color applied
14
14
  * - 2D Physics and collision system
15
15
  * - Sorted by renderOrder
16
16
  * - Objects can have children attached
@@ -289,7 +289,7 @@ class EngineObject
289
289
  }
290
290
 
291
291
  /** Destroy this object, destroy it's children, detach it's parent, and mark it for removal */
292
- destroy()
292
+ destroy()
293
293
  {
294
294
  if (this.destroyed)
295
295
  return;
@@ -10,12 +10,12 @@
10
10
  * @example
11
11
  * // create a particle emitter
12
12
  * let pos = vec2(2,3);
13
- * let particleEmiter = new ParticleEmitter
13
+ * let particleEmitter = new ParticleEmitter
14
14
  * (
15
- * pos, 0, 1, 0, 500, PI, // pos, angle, emitSize, emitTime, emitRate, emiteCone
16
- * tile(0, 16), // tileInfo
17
- * new Color(1,1,1), new Color(0,0,0), // colorStartA, colorStartB
18
- * new Color(1,1,1,0), new Color(0,0,0,0), // colorEndA, colorEndB
15
+ * pos, 0, 1, 0, 500, PI, // pos, angle, emitSize, emitTime, emitRate, emiteCone
16
+ * tile(0, 16), // tileInfo
17
+ * rgb(1,1,1), rgb(0,0,0), // colorStartA, colorStartB
18
+ * rgb(1,1,1,0), rgb(0,0,0,0), // colorEndA, colorEndB
19
19
  * 2, .2, .2, .1, .05, // particleTime, sizeStart, sizeEnd, particleSpeed, particleAngleSpeed
20
20
  * .99, 1, 1, PI, .05, // damping, angleDamping, gravityScale, particleCone, fadeRate,
21
21
  * .5, 1 // randomness, collide, additive, randomColorLinear, renderOrder
@@ -202,16 +202,17 @@ class ParticleEmitter extends EngineObject
202
202
 
203
203
  // build particle
204
204
  const particle = new Particle(pos, this.tileInfo, angle, colorStart, colorEnd, particleTime, sizeStart, sizeEnd, this.fadeRate, this.additive, this.trailScale, this.localSpace && this, this.particleDestroyCallback);
205
- particle.velocity = vec2().setAngle(velocityAngle, speed);
206
- particle.fadeRate = this.fadeRate;
207
- particle.damping = this.damping;
208
- particle.angleDamping = this.angleDamping;
209
- particle.elasticity = this.elasticity;
210
- particle.friction = this.friction;
211
- particle.gravityScale = this.gravityScale;
212
- particle.collideTiles = this.collideTiles;
213
- particle.renderOrder = this.renderOrder;
214
- particle.mirror = !!randInt(2);
205
+ particle.velocity = vec2().setAngle(velocityAngle, speed);
206
+ particle.angleVelocity = angleSpeed;
207
+ particle.fadeRate = this.fadeRate;
208
+ particle.damping = this.damping;
209
+ particle.angleDamping = this.angleDamping;
210
+ particle.elasticity = this.elasticity;
211
+ particle.friction = this.friction;
212
+ particle.gravityScale = this.gravityScale;
213
+ particle.collideTiles = this.collideTiles;
214
+ particle.renderOrder = this.renderOrder;
215
+ particle.mirror = !!randInt(2);
215
216
 
216
217
  // call particle create callaback
217
218
  this.particleCreateCallback && this.particleCreateCallback(particle);
@@ -83,7 +83,7 @@ let tileSizeDefault = vec2(16);
83
83
  * @type {Number}
84
84
  * @default
85
85
  * @memberof Settings */
86
- let tileFixBleedScale = .3;
86
+ let tileFixBleedScale = .1;
87
87
 
88
88
  ///////////////////////////////////////////////////////////////////////////////
89
89
  // Object settings
@@ -94,7 +94,7 @@ let tileFixBleedScale = .3;
94
94
  * @memberof Settings */
95
95
  let enablePhysicsSolver = true;
96
96
 
97
- /** Default object mass for collison calcuations (how heavy objects are)
97
+ /** Default object mass for collision calcuations (how heavy objects are)
98
98
  * @type {Number}
99
99
  * @default
100
100
  * @memberof Settings */
@@ -177,7 +177,7 @@ let touchGamepadEnable = false;
177
177
  * @memberof Settings */
178
178
  let touchGamepadAnalog = true;
179
179
 
180
- /** Size of virutal gamepad for touch devices in pixels
180
+ /** Size of virtual gamepad for touch devices in pixels
181
181
  * @type {Number}
182
182
  * @default
183
183
  * @memberof Settings */
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * LittleJS Tile Layer System
3
3
  * - Caches arrays of tiles to off screen canvas for fast rendering
4
- * - Unlimted numbers of layers, allocates canvases as needed
4
+ * - Unlimited numbers of layers, allocates canvases as needed
5
5
  * - Interfaces with EngineObject for collision
6
6
  * - Collision layer is separate from visible layers
7
7
  * - It is recommended to have a visible layer that matches the collision
@@ -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=(1,1)]
56
+ * @param {Vector2} [size=(0,0)]
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;
@@ -36,7 +36,7 @@ function min(valueA, valueB) { return Math.min(valueA, valueB); }
36
36
  * @memberof Utilities */
37
37
  function max(valueA, valueB) { return Math.max(valueA, valueB); }
38
38
 
39
- /** Returns the sign of value passed in (also returns 1 if 0)
39
+ /** Returns the sign of value passed in
40
40
  * @param {Number} value
41
41
  * @return {Number}
42
42
  * @memberof Utilities */
@@ -83,7 +83,7 @@ function lerp(percent, valueA, valueB) { return valueA + clamp(percent) * (value
83
83
  function distanceWrap(valueA, valueB, wrapSize=1)
84
84
  { const d = (valueA - valueB) % wrapSize; return d*2 % wrapSize - d; }
85
85
 
86
- /** Linearly interpolates between values passed in with wrappping
86
+ /** Linearly interpolates between values passed in with wrapping
87
87
  * @param {Number} percent
88
88
  * @param {Number} valueA
89
89
  * @param {Number} valueB
@@ -100,7 +100,7 @@ function lerpWrap(percent, valueA, valueB, wrapSize=1)
100
100
  * @memberof Utilities */
101
101
  function distanceAngle(angleA, angleB) { return distanceWrap(angleA, angleB, 2*PI); }
102
102
 
103
- /** Linearly interpolates between the angles passed in with wrappping
103
+ /** Linearly interpolates between the angles passed in with wrapping
104
104
  * @param {Number} percent
105
105
  * @param {Number} angleA
106
106
  * @param {Number} angleB
@@ -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
@@ -193,8 +193,6 @@ function glCreateTexture(image)
193
193
  const filter = canvasPixelated ? gl_NEAREST : gl_LINEAR;
194
194
  glContext.texParameteri(gl_TEXTURE_2D, gl_TEXTURE_MIN_FILTER, filter);
195
195
  glContext.texParameteri(gl_TEXTURE_2D, gl_TEXTURE_MAG_FILTER, filter);
196
- glContext.texParameteri(gl_TEXTURE_2D, gl_TEXTURE_WRAP_S, gl_CLAMP_TO_EDGE);
197
- glContext.texParameteri(gl_TEXTURE_2D, gl_TEXTURE_WRAP_T, gl_CLAMP_TO_EDGE);
198
196
 
199
197
  return texture;
200
198
  }
@@ -251,7 +249,7 @@ function glDraw(x, y, sizeX, sizeY, angle, uv0X, uv0Y, uv1X, uv1Y, rgba, rgbaAdd
251
249
  ASSERT(typeof rgba == 'number' && typeof rgbaAdditive == 'number', 'invalid color');
252
250
 
253
251
  // flush if there is not enough room or if different blend mode
254
- if (glInstanceCount >= gl_MAX_INSTANCES-1 || glBatchAdditive != glAdditive)
252
+ if (glInstanceCount >= gl_MAX_INSTANCES || glBatchAdditive != glAdditive)
255
253
  glFlush();
256
254
 
257
255
  let offset = glInstanceCount * gl_INDICIES_PER_INSTANCE;
@@ -272,7 +270,7 @@ function glDraw(x, y, sizeX, sizeY, angle, uv0X, uv0Y, uv1X, uv1Y, rgba, rgbaAdd
272
270
  ///////////////////////////////////////////////////////////////////////////////
273
271
  // post processing - can be enabled to pass other canvases through a final shader
274
272
 
275
- let glPostShader, glPostArrayBuffer, glPostTexture, glPostIncludeOverlay;
273
+ let glPostShader, glPostTexture, glPostIncludeOverlay;
276
274
 
277
275
  /** Set up a post processing shader
278
276
  * @param {String} shaderCode
@@ -308,7 +306,6 @@ function glInitPostProcess(shaderCode, includeOverlay=false)
308
306
  );
309
307
 
310
308
  // create buffer and texture
311
- glPostArrayBuffer = glContext.createBuffer();
312
309
  glPostTexture = glCreateTexture(undefined);
313
310
  glPostIncludeOverlay = includeOverlay;
314
311
 
@@ -380,10 +377,7 @@ gl_NEAREST = 9728,
380
377
  gl_LINEAR = 9729,
381
378
  gl_TEXTURE_MAG_FILTER = 10240,
382
379
  gl_TEXTURE_MIN_FILTER = 10241,
383
- gl_TEXTURE_WRAP_S = 10242,
384
- gl_TEXTURE_WRAP_T = 10243,
385
380
  gl_COLOR_BUFFER_BIT = 16384,
386
- gl_CLAMP_TO_EDGE = 33071,
387
381
  gl_TEXTURE0 = 33984,
388
382
  gl_ARRAY_BUFFER = 34962,
389
383
  gl_STATIC_DRAW = 35044,