littlejsengine 1.17.10 → 1.17.11

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.
@@ -33,7 +33,7 @@ const engineName = 'LittleJS';
33
33
  * @type {string}
34
34
  * @default
35
35
  * @memberof Engine */
36
- const engineVersion = '1.17.10';
36
+ const engineVersion = '1.17.11';
37
37
 
38
38
  /** Frames per second to update
39
39
  * @type {number}
@@ -396,33 +396,11 @@ async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, game
396
396
  workReadContext = workReadCanvas.getContext('2d', { willReadFrequently: true });
397
397
 
398
398
  // create promises for loading images
399
- const promises = imageSources.map((src, textureIndex)=>
400
- new Promise(resolve =>
401
- {
402
- ASSERT(isString(src), 'imageSources must be an array of strings');
403
-
404
- const image = new Image;
405
- image.onerror = image.onload = ()=>
406
- {
407
- const textureInfo = new TextureInfo(image);
408
- textureInfos[textureIndex] = textureInfo;
409
- resolve();
410
- }
411
- image.crossOrigin = 'anonymous';
412
- image.src = src;
413
- })
414
- );
399
+ const promises = imageSources.map((src, i)=> loadTexture(i, src));
415
400
 
401
+ // no images to load
416
402
  if (!imageSources.length)
417
- {
418
- // no images to load
419
- promises.push(new Promise(resolve =>
420
- {
421
- const textureInfo = new TextureInfo(new Image);
422
- textureInfos[0] = textureInfo;
423
- resolve();
424
- }));
425
- }
403
+ promises.push(loadTexture(0));
426
404
 
427
405
  // load engine font image
428
406
  promises.push(fontImageInit());
@@ -649,7 +627,7 @@ function drawEngineLogo(t)
649
627
  x.lineWidth = .1+p*3.9;
650
628
  x.textAlign = 'center';
651
629
  x.textBaseline = 'top';
652
- rect(11,Y+2,59,8*p,-1);
630
+ rect(11,Y+1,59,8*p,-1);
653
631
  x.beginPath();
654
632
 
655
633
  let w2 = 0;
@@ -672,8 +650,8 @@ function drawEngineLogo(t)
672
650
  rect(25,15,8,25,-1); // cab front
673
651
  rect(10,40,15,-25,1); // cab back
674
652
  rect(14,21,7,9,2); // cab window
675
- rect(38,15,6,6,2); // little stack
676
-
653
+ rect(38,20,6,-6,2); // little stack
654
+
677
655
  // big stack
678
656
  rect(49,20,10,-6,0);
679
657
  const stackPoints = [vec2(44,8),vec2(64,8),vec2(59,8+6*p),vec2(49,8+6*p)];
@@ -681,8 +659,8 @@ function drawEngineLogo(t)
681
659
  rect(44,8,20,-7,0);
682
660
 
683
661
  // engine
684
- for (let i=5;i--;) circle(59-i*6,30,10,0,9,1,0);
685
- circle(59,30,4,0,9,2); // light
662
+ for (let i=5;i--;) circle(59-i*6*p,30,10,0,2*PI,1,0);
663
+ circle(59,30,4,0,7,2); // light
686
664
 
687
665
  // engine outline
688
666
  rect(35,20,24,0); // top
@@ -693,9 +671,7 @@ function drawEngineLogo(t)
693
671
  rect(17,40,43,14,-1); // bottom center
694
672
 
695
673
  // wheels
696
- for (let i=3;i--;)
697
- for (let j=2;j--;)
698
- circle(15*i+17,47,j?7:1,PI,3*PI,2);
674
+ for (let i=3;i--;) for (let j=2;j--;) circle(17+15*i,47,j?7:1,0,2*PI,2);
699
675
 
700
676
  // cowcatcher
701
677
  for (let i=2;i--;)
@@ -1012,7 +988,9 @@ function wave(frequency=1, amplitude=1, t=time, offset=0)
1012
988
  function isNumber(n) { return typeof n === 'number' && !isNaN(n); }
1013
989
 
1014
990
  /**
1015
- * Check if object is a valid string or can be converted to one
991
+ * Check if object can be converted to a string (has a toString method)
992
+ * - Returns true for strings, numbers, and most objects
993
+ * - Returns false for null and undefined
1016
994
  * @param {any} s
1017
995
  * @return {boolean}
1018
996
  * @memberof Math */
@@ -1089,7 +1067,7 @@ function lineTest(posStart, posEnd, testFunction, normal)
1089
1067
  if (stepX < 0)
1090
1068
  hitPos.x -= e;
1091
1069
  }
1092
- if (stepY < 0)
1070
+ else if (stepY < 0)
1093
1071
  hitPos.y -= e;
1094
1072
 
1095
1073
  // set normal
@@ -1796,7 +1774,7 @@ class Color
1796
1774
  * @memberof Math */
1797
1775
  const WHITE = debugProtectConstant(rgb());
1798
1776
 
1799
- /** Color - Clear White #757474ff with 0 alpha
1777
+ /** Color - Clear White #ffffff00 with 0 alpha
1800
1778
  * @type {Color}
1801
1779
  * @memberof Math */
1802
1780
  const CLEAR_WHITE = debugProtectConstant(rgb(1,1,1,0));
@@ -1806,7 +1784,7 @@ const CLEAR_WHITE = debugProtectConstant(rgb(1,1,1,0));
1806
1784
  * @memberof Math */
1807
1785
  const BLACK = debugProtectConstant(rgb(0,0,0));
1808
1786
 
1809
- /** Color - Clear Black #000000 with 0 alpha
1787
+ /** Color - Clear Black #00000000 with 0 alpha
1810
1788
  * @type {Color}
1811
1789
  * @memberof Math */
1812
1790
  const CLEAR_BLACK = debugProtectConstant(rgb(0,0,0,0));
@@ -3901,6 +3879,31 @@ function drawTextScreen(text, pos, size, color=WHITE, lineWidth=0, lineColor=BLA
3901
3879
  ///////////////////////////////////////////////////////////////////////////////
3902
3880
  // Drawing utilities
3903
3881
 
3882
+ /** Load a texture at a specific index
3883
+ * @param {number} textureIndex - Index to store the texture at
3884
+ * @param {string} [src] - Image source path
3885
+ * @return {Promise} Promise that resolves when texture is loaded
3886
+ * @memberof Draw */
3887
+ async function loadTexture(textureIndex, src)
3888
+ {
3889
+ ASSERT(isNumber(textureIndex), 'textureIndex must be a number');
3890
+ ASSERT(!textureInfos[textureIndex], 'textureIndex is already loaded!');
3891
+ ASSERT(!src || isString(src), 'image src must be a string');
3892
+
3893
+ const image = new Image;
3894
+ if (src)
3895
+ {
3896
+ await new Promise(resolve =>
3897
+ {
3898
+ image.onerror = image.onload = resolve;
3899
+ image.crossOrigin = 'anonymous';
3900
+ image.src = src;
3901
+ });
3902
+ }
3903
+
3904
+ textureInfos[textureIndex] = new TextureInfo(image);
3905
+ }
3906
+
3904
3907
  /** Convert from screen to world space coordinates
3905
3908
  * @param {Vector2} screenPos
3906
3909
  * @return {Vector2}
@@ -4281,23 +4284,20 @@ class FontImage
4281
4284
  }
4282
4285
 
4283
4286
  // load engine font, called automatically on startup
4284
- function fontImageInit()
4287
+ async function fontImageInit()
4285
4288
  {
4286
- return new Promise(resolve =>
4289
+ const image = new Image;
4290
+ await new Promise(resolve =>
4287
4291
  {
4288
- // create the engine font
4289
- const image = new Image;
4290
- image.onerror = image.onload = ()=>
4291
- {
4292
- const tilePos=vec2(), tileSize=vec2(8), padding=1, bleed=0;
4293
- const textureInfo = new TextureInfo(image);
4294
- const tileInfo = new TileInfo(tilePos, tileSize, textureInfo, padding, bleed);
4295
- engineFontImage = new FontImage(tileInfo);
4296
- resolve();
4297
- }
4292
+ image.onerror = image.onload = resolve;
4298
4293
  image.crossOrigin = 'anonymous';
4299
4294
  image.src = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAUAAAAAeAQMAAABnrVXaAAAABlBMVEUAAAD///+l2Z/dAAAAAXRSTlMAQObYZgAAAjpJREFUOMu9kzFu2zAUhn+CAROgqrk+B2l0BWYxMjlXeYaAtFtbdA1sGgHqRQfI0CNkSG5AwYB0BQ8d5Bsomwah6CPVeGg6tEPzAxLwyI+P78cP4u9lNO9OoMKnLMOobG5020/yaj/MrRcCGh1gBbyiLTPJEYaIiom5KM9Jq7KgynMGtb6L4GL4MF2H4LQKCXTvDVw2I4MsgZT7QLExdiutH+D08VOP3INXRrWX1/mmpbkNgAPYRVANb4xpcegYvhiNbIXauQICEjBuYLfMakaakWQeXxiZ0VDtuJCKs3ztMV59QtsHJNcRxDzfdL21ty3PrfIcXTN+E+GFAv6T5nbT9jd50/WFxb5ksdAv49qS6ouymG66ji08UMT6moykYLAo+V0j23GN4m829ZySAD5K7QsBfQTvOG8eE+gTeGYRAmnNAubN3hf5Zv9tJWDHp/VTuaSm7SN4fyINQqaNO3RMVxvpSPXnOChnRNvFcGY0gnwiPswYwTKVPE0zVtX3mTEIOoFzaqLrGuJaV+Uqumb71fVk/VoOH3cdLNQP/FHi8hV0CQNoqBZsUPlLPMsdCJro9QAaQQ0woDy9BJm0eTxCFnO9srcYlhNVlfR2EyTrph1uUtbUtAJifwRgrKuYdXVHeb0YI3QpawohQHkloI3J5FuVwI5ORxC9k2Tuz9Ir1IjgeIPGMHYkAZe2RuYkmWFmt3gGbTPOmBUWVTmRmHtGrfpzG/yuQNOKa6gBB/WA9khitPgl6/GP+gl2Af6tCbvaygAAAABJRU5ErkJggg==';
4300
4295
  });
4296
+
4297
+ const tilePos=vec2(), tileSize=vec2(8), padding=1, bleed=0;
4298
+ const textureInfo = new TextureInfo(image);
4299
+ const tileInfo = new TileInfo(tilePos, tileSize, textureInfo, padding, bleed);
4300
+ engineFontImage = new FontImage(tileInfo);
4301
4301
  }
4302
4302
  /**
4303
4303
  * LittleJS Input System
@@ -5280,7 +5280,7 @@ class Sound
5280
5280
 
5281
5281
  /** Loads a sound from a URL and decodes it into sample data.
5282
5282
  * @param {string} filename
5283
- * @return {Promise<void>} */
5283
+ * @return {Promise} */
5284
5284
  async loadSound(filename)
5285
5285
  {
5286
5286
  const response = await fetch(filename);
@@ -5338,7 +5338,7 @@ class Sound
5338
5338
  * // Control the individual instance
5339
5339
  * instance.setVolume(.5);
5340
5340
  * instance.pause();
5341
- * instance.unpause();
5341
+ * instance.resume();
5342
5342
  * instance.stop();
5343
5343
  */
5344
5344
  class SoundInstance
@@ -5452,7 +5452,7 @@ class SoundInstance
5452
5452
  this.startTime = undefined;
5453
5453
  }
5454
5454
 
5455
- /** Unpauses this sound instance */
5455
+ /** Resume this sound instance */
5456
5456
  resume()
5457
5457
  {
5458
5458
  if (!this.isPaused()) return;
@@ -5466,8 +5466,8 @@ class SoundInstance
5466
5466
  */
5467
5467
  isPlaying() { return !!this.source; }
5468
5468
 
5469
- /** Check if this instance is paused and was not stopped
5470
- * @return {boolean} - True if paused
5469
+ /** Check if this instance is paused or stopped (not currently playing)
5470
+ * @return {boolean} - True if not playing
5471
5471
  */
5472
5472
  isPaused() { return !this.isPlaying(); }
5473
5473
 
@@ -6393,7 +6393,7 @@ class TileCollisionLayer extends TileLayer
6393
6393
  collisionTest(pos, size=new Vector2, callbackObject)
6394
6394
  {
6395
6395
  ASSERT(isVector2(pos) && isVector2(size), 'pos and size must be Vector2s');
6396
- ASSERT(!callbackObject || typeof callbackObject === 'function' || callbackObject instanceof EngineObject, 'callbackObject must be a funtion or EngineObject');
6396
+ ASSERT(!callbackObject || typeof callbackObject === 'function' || callbackObject instanceof EngineObject, 'callbackObject must be a function or EngineObject');
6397
6397
 
6398
6398
  // make function to check for collision
6399
6399
  const collisionTest = callbackObject ? typeof callbackObject === 'function' ?
@@ -6430,7 +6430,7 @@ class TileCollisionLayer extends TileLayer
6430
6430
  collisionRaycast(posStart, posEnd, callbackObject, normal)
6431
6431
  {
6432
6432
  ASSERT(isVector2(posStart) && isVector2(posEnd), 'positions must be Vector2s');
6433
- ASSERT(!callbackObject || typeof callbackObject === 'function' || callbackObject instanceof EngineObject, 'callbackObject must be a funtion or EngineObject');
6433
+ ASSERT(!callbackObject || typeof callbackObject === 'function' || callbackObject instanceof EngineObject, 'callbackObject must be a function or EngineObject');
6434
6434
 
6435
6435
  // make function to check for collision
6436
6436
  const collisionTest = callbackObject ? typeof callbackObject === 'function' ?
@@ -9761,7 +9761,7 @@ class UIScrollbar extends UIObject
9761
9761
  else
9762
9762
  {
9763
9763
  // draw the scrollbar handle
9764
- const value = isHorizontal ? this.value : 1 - this.value;
9764
+ const value = clamp(isHorizontal ? this.value : 1 - this.value);
9765
9765
  const p = (barWidth - handleWidth) * (value - .5);
9766
9766
  const pos = this.pos.add(isHorizontal ? vec2(p, 0) : vec2(0, p));
9767
9767
  const color = this.disabled ? this.disabledColor : this.handleColor;
@@ -9830,12 +9830,11 @@ class UIVideo extends UIObject
9830
9830
 
9831
9831
  /** Play or resume the video
9832
9832
  * @return {Promise} Promise that resolves when playback starts */
9833
- play()
9833
+ async play()
9834
9834
  {
9835
9835
  // try to play the video, catch any errors (autoplay may be blocked)
9836
- const promise = this.video.play();
9837
- promise?.catch(()=>{});
9838
- return promise;
9836
+ try { await this.video.play(); }
9837
+ catch(e) {}
9839
9838
  }
9840
9839
 
9841
9840
  /** Pause the video */
@@ -7,7 +7,7 @@
7
7
  </head><body>
8
8
 
9
9
  <!-- LittleJS Engine -->
10
- <script src=../../dist/littlejs.js?1.17.10></script>
10
+ <script src=../../dist/littlejs.js?1.17.11></script>
11
11
 
12
12
  <!-- Add your game scripts here -->
13
- <script src=game.js?1.17.10></script>
13
+ <script src=game.js?1.17.11></script>
Binary file
Binary file
@@ -1,5 +1,5 @@
1
1
  <!DOCTYPE html><meta charset=utf-8><body>
2
- <script src=../../dist/littlejs.js?1.17.10></script>
2
+ <script src=../../dist/littlejs.js?1.17.11></script>
3
3
  <script src=../../dist/box2d.wasm.js></script>
4
4
 
5
5
  <!-- LittleJS Engine Source
@@ -1,7 +1,7 @@
1
1
  function gameRender()
2
2
  {
3
3
  // draw text with built in engine font image
4
- const font = engineFontImage
4
+ const font = engineFontImage;
5
5
  font.drawText('Engine Font', vec2(0,3), 2);
6
6
 
7
7
  // show every character in the font
@@ -7,7 +7,7 @@
7
7
  </head><body>
8
8
 
9
9
  <!-- LittleJS Engine -->
10
- <script src=../../dist/littlejs.js?1.17.9></script>
10
+ <script src=../../dist/littlejs.js?1.17.11></script>
11
11
 
12
12
  <!-- LittleJS Engine Source
13
13
  <script src=../../src/engine.js></script>
@@ -32,4 +32,4 @@
32
32
  -->
33
33
 
34
34
  <!-- Add your game scripts here -->
35
- <script src=game.js?1.17.10></script>
35
+ <script src=game.js?1.17.11></script>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "littlejsengine",
3
- "version": "1.17.10",
3
+ "version": "1.17.11",
4
4
  "description": "LittleJS - Tiny and Fast HTML5 Game Engine",
5
5
  "main": "dist/littlejs.esm.js",
6
6
  "types": "dist/littlejs.d.ts",
@@ -38,6 +38,7 @@ export
38
38
  Box2dObject,
39
39
  Box2dStaticObject,
40
40
  Box2dKinematicObject,
41
+ Box2dTileLayer,
41
42
  Box2dRaycastResult,
42
43
  Box2dJoint,
43
44
  Box2dTargetJoint,
@@ -1209,7 +1209,7 @@ class UIScrollbar extends UIObject
1209
1209
  else
1210
1210
  {
1211
1211
  // draw the scrollbar handle
1212
- const value = isHorizontal ? this.value : 1 - this.value;
1212
+ const value = clamp(isHorizontal ? this.value : 1 - this.value);
1213
1213
  const p = (barWidth - handleWidth) * (value - .5);
1214
1214
  const pos = this.pos.add(isHorizontal ? vec2(p, 0) : vec2(0, p));
1215
1215
  const color = this.disabled ? this.disabledColor : this.handleColor;
@@ -1278,12 +1278,11 @@ class UIVideo extends UIObject
1278
1278
 
1279
1279
  /** Play or resume the video
1280
1280
  * @return {Promise} Promise that resolves when playback starts */
1281
- play()
1281
+ async play()
1282
1282
  {
1283
1283
  // try to play the video, catch any errors (autoplay may be blocked)
1284
- const promise = this.video.play();
1285
- promise?.catch(()=>{});
1286
- return promise;
1284
+ try { await this.video.play(); }
1285
+ catch(e) {}
1287
1286
  }
1288
1287
 
1289
1288
  /** Pause the video */
package/src/engine.js CHANGED
@@ -30,7 +30,7 @@ const engineName = 'LittleJS';
30
30
  * @type {string}
31
31
  * @default
32
32
  * @memberof Engine */
33
- const engineVersion = '1.17.10';
33
+ const engineVersion = '1.17.11';
34
34
 
35
35
  /** Frames per second to update
36
36
  * @type {number}
@@ -393,33 +393,11 @@ async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, game
393
393
  workReadContext = workReadCanvas.getContext('2d', { willReadFrequently: true });
394
394
 
395
395
  // create promises for loading images
396
- const promises = imageSources.map((src, textureIndex)=>
397
- new Promise(resolve =>
398
- {
399
- ASSERT(isString(src), 'imageSources must be an array of strings');
400
-
401
- const image = new Image;
402
- image.onerror = image.onload = ()=>
403
- {
404
- const textureInfo = new TextureInfo(image);
405
- textureInfos[textureIndex] = textureInfo;
406
- resolve();
407
- }
408
- image.crossOrigin = 'anonymous';
409
- image.src = src;
410
- })
411
- );
396
+ const promises = imageSources.map((src, i)=> loadTexture(i, src));
412
397
 
398
+ // no images to load
413
399
  if (!imageSources.length)
414
- {
415
- // no images to load
416
- promises.push(new Promise(resolve =>
417
- {
418
- const textureInfo = new TextureInfo(new Image);
419
- textureInfos[0] = textureInfo;
420
- resolve();
421
- }));
422
- }
400
+ promises.push(loadTexture(0));
423
401
 
424
402
  // load engine font image
425
403
  promises.push(fontImageInit());
@@ -646,7 +624,7 @@ function drawEngineLogo(t)
646
624
  x.lineWidth = .1+p*3.9;
647
625
  x.textAlign = 'center';
648
626
  x.textBaseline = 'top';
649
- rect(11,Y+2,59,8*p,-1);
627
+ rect(11,Y+1,59,8*p,-1);
650
628
  x.beginPath();
651
629
 
652
630
  let w2 = 0;
@@ -669,8 +647,8 @@ function drawEngineLogo(t)
669
647
  rect(25,15,8,25,-1); // cab front
670
648
  rect(10,40,15,-25,1); // cab back
671
649
  rect(14,21,7,9,2); // cab window
672
- rect(38,15,6,6,2); // little stack
673
-
650
+ rect(38,20,6,-6,2); // little stack
651
+
674
652
  // big stack
675
653
  rect(49,20,10,-6,0);
676
654
  const stackPoints = [vec2(44,8),vec2(64,8),vec2(59,8+6*p),vec2(49,8+6*p)];
@@ -678,7 +656,7 @@ function drawEngineLogo(t)
678
656
  rect(44,8,20,-7,0);
679
657
 
680
658
  // engine
681
- for (let i=5;i--;) circle(59-i*6,30,10,0,7,1,0);
659
+ for (let i=5;i--;) circle(59-i*6*p,30,10,0,2*PI,1,0);
682
660
  circle(59,30,4,0,7,2); // light
683
661
 
684
662
  // engine outline
@@ -690,7 +668,7 @@ function drawEngineLogo(t)
690
668
  rect(17,40,43,14,-1); // bottom center
691
669
 
692
670
  // wheels
693
- for (let i=3;i--;) for (let j=2;j--;) circle(15*i+17,47,j?7:1,0,7,2);
671
+ for (let i=3;i--;) for (let j=2;j--;) circle(17+15*i,47,j?7:1,0,2*PI,2);
694
672
 
695
673
  // cowcatcher
696
674
  for (let i=2;i--;)
@@ -204,7 +204,7 @@ class Sound
204
204
 
205
205
  /** Loads a sound from a URL and decodes it into sample data.
206
206
  * @param {string} filename
207
- * @return {Promise<void>} */
207
+ * @return {Promise} */
208
208
  async loadSound(filename)
209
209
  {
210
210
  const response = await fetch(filename);
@@ -262,7 +262,7 @@ class Sound
262
262
  * // Control the individual instance
263
263
  * instance.setVolume(.5);
264
264
  * instance.pause();
265
- * instance.unpause();
265
+ * instance.resume();
266
266
  * instance.stop();
267
267
  */
268
268
  class SoundInstance
@@ -376,7 +376,7 @@ class SoundInstance
376
376
  this.startTime = undefined;
377
377
  }
378
378
 
379
- /** Unpauses this sound instance */
379
+ /** Resume this sound instance */
380
380
  resume()
381
381
  {
382
382
  if (!this.isPaused()) return;
@@ -390,8 +390,8 @@ class SoundInstance
390
390
  */
391
391
  isPlaying() { return !!this.source; }
392
392
 
393
- /** Check if this instance is paused and was not stopped
394
- * @return {boolean} - True if paused
393
+ /** Check if this instance is paused or stopped (not currently playing)
394
+ * @return {boolean} - True if not playing
395
395
  */
396
396
  isPaused() { return !this.isPlaying(); }
397
397
 
@@ -158,7 +158,7 @@ function debugPoint(pos, color, time, angle, screenSpace=false)
158
158
  function debugLine(posA, posB, color, width=.1, time, screenSpace=false)
159
159
  {
160
160
  ASSERT(isVector2(posA), 'posA must be a vec2');
161
- ASSERT(isVector2(posB), 'posB must be sa vec2');
161
+ ASSERT(isVector2(posB), 'posB must be a vec2');
162
162
  ASSERT(isNumber(width), 'width must be a number');
163
163
 
164
164
  const halfDelta = vec2((posB.x - posA.x)/2, (posB.y - posA.y)/2);
package/src/engineDraw.js CHANGED
@@ -729,6 +729,31 @@ function drawTextScreen(text, pos, size, color=WHITE, lineWidth=0, lineColor=BLA
729
729
  ///////////////////////////////////////////////////////////////////////////////
730
730
  // Drawing utilities
731
731
 
732
+ /** Load a texture at a specific index
733
+ * @param {number} textureIndex - Index to store the texture at
734
+ * @param {string} [src] - Image source path
735
+ * @return {Promise} Promise that resolves when texture is loaded
736
+ * @memberof Draw */
737
+ async function loadTexture(textureIndex, src)
738
+ {
739
+ ASSERT(isNumber(textureIndex), 'textureIndex must be a number');
740
+ ASSERT(!textureInfos[textureIndex], 'textureIndex is already loaded!');
741
+ ASSERT(!src || isString(src), 'image src must be a string');
742
+
743
+ const image = new Image;
744
+ if (src)
745
+ {
746
+ await new Promise(resolve =>
747
+ {
748
+ image.onerror = image.onload = resolve;
749
+ image.crossOrigin = 'anonymous';
750
+ image.src = src;
751
+ });
752
+ }
753
+
754
+ textureInfos[textureIndex] = new TextureInfo(image);
755
+ }
756
+
732
757
  /** Convert from screen to world space coordinates
733
758
  * @param {Vector2} screenPos
734
759
  * @return {Vector2}
@@ -1109,21 +1134,18 @@ class FontImage
1109
1134
  }
1110
1135
 
1111
1136
  // load engine font, called automatically on startup
1112
- function fontImageInit()
1137
+ async function fontImageInit()
1113
1138
  {
1114
- return new Promise(resolve =>
1139
+ const image = new Image;
1140
+ await new Promise(resolve =>
1115
1141
  {
1116
- // create the engine font
1117
- const image = new Image;
1118
- image.onerror = image.onload = ()=>
1119
- {
1120
- const tilePos=vec2(), tileSize=vec2(8), padding=1, bleed=0;
1121
- const textureInfo = new TextureInfo(image);
1122
- const tileInfo = new TileInfo(tilePos, tileSize, textureInfo, padding, bleed);
1123
- engineFontImage = new FontImage(tileInfo);
1124
- resolve();
1125
- }
1142
+ image.onerror = image.onload = resolve;
1126
1143
  image.crossOrigin = 'anonymous';
1127
1144
  image.src = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAUAAAAAeAQMAAABnrVXaAAAABlBMVEUAAAD///+l2Z/dAAAAAXRSTlMAQObYZgAAAjpJREFUOMu9kzFu2zAUhn+CAROgqrk+B2l0BWYxMjlXeYaAtFtbdA1sGgHqRQfI0CNkSG5AwYB0BQ8d5Bsomwah6CPVeGg6tEPzAxLwyI+P78cP4u9lNO9OoMKnLMOobG5020/yaj/MrRcCGh1gBbyiLTPJEYaIiom5KM9Jq7KgynMGtb6L4GL4MF2H4LQKCXTvDVw2I4MsgZT7QLExdiutH+D08VOP3INXRrWX1/mmpbkNgAPYRVANb4xpcegYvhiNbIXauQICEjBuYLfMakaakWQeXxiZ0VDtuJCKs3ztMV59QtsHJNcRxDzfdL21ty3PrfIcXTN+E+GFAv6T5nbT9jd50/WFxb5ksdAv49qS6ouymG66ji08UMT6moykYLAo+V0j23GN4m829ZySAD5K7QsBfQTvOG8eE+gTeGYRAmnNAubN3hf5Zv9tJWDHp/VTuaSm7SN4fyINQqaNO3RMVxvpSPXnOChnRNvFcGY0gnwiPswYwTKVPE0zVtX3mTEIOoFzaqLrGuJaV+Uqumb71fVk/VoOH3cdLNQP/FHi8hV0CQNoqBZsUPlLPMsdCJro9QAaQQ0woDy9BJm0eTxCFnO9srcYlhNVlfR2EyTrph1uUtbUtAJifwRgrKuYdXVHeb0YI3QpawohQHkloI3J5FuVwI5ORxC9k2Tuz9Ir1IjgeIPGMHYkAZe2RuYkmWFmt3gGbTPOmBUWVTmRmHtGrfpzG/yuQNOKa6gBB/WA9khitPgl6/GP+gl2Af6tCbvaygAAAABJRU5ErkJggg==';
1128
1145
  });
1146
+
1147
+ const tilePos=vec2(), tileSize=vec2(8), padding=1, bleed=0;
1148
+ const textureInfo = new TextureInfo(image);
1149
+ const tileInfo = new TileInfo(tilePos, tileSize, textureInfo, padding, bleed);
1150
+ engineFontImage = new FontImage(tileInfo);
1129
1151
  }
package/src/engineMath.js CHANGED
@@ -267,7 +267,9 @@ function wave(frequency=1, amplitude=1, t=time, offset=0)
267
267
  function isNumber(n) { return typeof n === 'number' && !isNaN(n); }
268
268
 
269
269
  /**
270
- * Check if object is a valid string or can be converted to one
270
+ * Check if object can be converted to a string (has a toString method)
271
+ * - Returns true for strings, numbers, and most objects
272
+ * - Returns false for null and undefined
271
273
  * @param {any} s
272
274
  * @return {boolean}
273
275
  * @memberof Math */
@@ -344,7 +346,7 @@ function lineTest(posStart, posEnd, testFunction, normal)
344
346
  if (stepX < 0)
345
347
  hitPos.x -= e;
346
348
  }
347
- if (stepY < 0)
349
+ else if (stepY < 0)
348
350
  hitPos.y -= e;
349
351
 
350
352
  // set normal
@@ -1051,7 +1053,7 @@ class Color
1051
1053
  * @memberof Math */
1052
1054
  const WHITE = debugProtectConstant(rgb());
1053
1055
 
1054
- /** Color - Clear White #757474ff with 0 alpha
1056
+ /** Color - Clear White #ffffff00 with 0 alpha
1055
1057
  * @type {Color}
1056
1058
  * @memberof Math */
1057
1059
  const CLEAR_WHITE = debugProtectConstant(rgb(1,1,1,0));
@@ -1061,7 +1063,7 @@ const CLEAR_WHITE = debugProtectConstant(rgb(1,1,1,0));
1061
1063
  * @memberof Math */
1062
1064
  const BLACK = debugProtectConstant(rgb(0,0,0));
1063
1065
 
1064
- /** Color - Clear Black #000000 with 0 alpha
1066
+ /** Color - Clear Black #00000000 with 0 alpha
1065
1067
  * @type {Color}
1066
1068
  * @memberof Math */
1067
1069
  const CLEAR_BLACK = debugProtectConstant(rgb(0,0,0,0));
@@ -643,7 +643,7 @@ class TileCollisionLayer extends TileLayer
643
643
  collisionTest(pos, size=new Vector2, callbackObject)
644
644
  {
645
645
  ASSERT(isVector2(pos) && isVector2(size), 'pos and size must be Vector2s');
646
- ASSERT(!callbackObject || typeof callbackObject === 'function' || callbackObject instanceof EngineObject, 'callbackObject must be a funtion or EngineObject');
646
+ ASSERT(!callbackObject || typeof callbackObject === 'function' || callbackObject instanceof EngineObject, 'callbackObject must be a function or EngineObject');
647
647
 
648
648
  // make function to check for collision
649
649
  const collisionTest = callbackObject ? typeof callbackObject === 'function' ?
@@ -680,7 +680,7 @@ class TileCollisionLayer extends TileLayer
680
680
  collisionRaycast(posStart, posEnd, callbackObject, normal)
681
681
  {
682
682
  ASSERT(isVector2(posStart) && isVector2(posEnd), 'positions must be Vector2s');
683
- ASSERT(!callbackObject || typeof callbackObject === 'function' || callbackObject instanceof EngineObject, 'callbackObject must be a funtion or EngineObject');
683
+ ASSERT(!callbackObject || typeof callbackObject === 'function' || callbackObject instanceof EngineObject, 'callbackObject must be a function or EngineObject');
684
684
 
685
685
  // make function to check for collision
686
686
  const collisionTest = callbackObject ? typeof callbackObject === 'function' ?