littlejsengine 1.18.12 → 1.18.15

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "littlejsengine",
3
- "version": "1.18.12",
3
+ "version": "1.18.15",
4
4
  "description": "LittleJS - Tiny and Fast HTML5 Game Engine",
5
5
  "main": "dist/littlejs.esm.js",
6
6
  "types": "dist/littlejs.d.ts",
@@ -40,6 +40,9 @@
40
40
  "build": "node src/engineBuild.mjs",
41
41
  "test": "node --test --import ./test/setup.mjs \"test/**/*.test.mjs\""
42
42
  },
43
+ "engines": {
44
+ "node": ">=18"
45
+ },
43
46
  "devDependencies": {
44
47
  "bestzip": "^2.2.1",
45
48
  "electron": "^38.2.0",
package/plugins/box2d.js CHANGED
@@ -83,6 +83,11 @@ class Box2dObject extends EngineObject
83
83
  // destroy physics body, fixtures, and joints
84
84
  ASSERT(this.body, 'Box2dObject has no body to destroy');
85
85
  box2d.world.DestroyBody(this.body);
86
+
87
+ // remove from tracked list so paused / headless sessions don't leak
88
+ const i = box2d.objects.indexOf(this);
89
+ if (i >= 0)
90
+ box2d.objects.splice(i, 1);
86
91
  super.destroy();
87
92
  }
88
93
 
@@ -171,7 +176,9 @@ class Box2dObject extends EngineObject
171
176
  /** Add a box shape to the body
172
177
  * @param {Vector2} [size]
173
178
  * @param {Vector2} [offset]
174
- * @param {number} [angle]
179
+ * @param {number} [angle] - LittleJS convention (clockwise positive).
180
+ * Negated internally to match Box2D's CCW-positive convention so the
181
+ * fixture aligns with the same angle passed to drawRect/drawTile.
175
182
  * @param {number} [density]
176
183
  * @param {number} [friction]
177
184
  * @param {number} [restitution]
@@ -184,7 +191,7 @@ class Box2dObject extends EngineObject
184
191
  ASSERT(isNumber(angle), 'angle must be a number');
185
192
 
186
193
  const shape = new box2d.instance.b2PolygonShape();
187
- shape.SetAsBox(size.x/2, size.y/2, box2d.vec2dTo(offset), angle);
194
+ shape.SetAsBox(size.x/2, size.y/2, box2d.vec2dTo(offset), -angle);
188
195
  return this.addShape(shape, density, friction, restitution, isSensor);
189
196
  }
190
197
 
@@ -486,9 +493,10 @@ class Box2dObject extends EngineObject
486
493
  {
487
494
  const data = new box2d.instance.b2MassData();
488
495
  this.body.GetMassData(data);
489
- localCenter && data.set_center(box2d.vec2dTo(localCenter));
490
- mass && data.set_mass(mass);
491
- momentOfInertia && data.set_I(momentOfInertia);
496
+ // use !== undefined so setMass(0) (static-equivalent) isn't silently ignored
497
+ if (localCenter !== undefined) data.set_center(box2d.vec2dTo(localCenter));
498
+ if (mass !== undefined) data.set_mass(mass);
499
+ if (momentOfInertia !== undefined) data.set_I(momentOfInertia);
492
500
  this.body.SetMassData(data);
493
501
  }
494
502
 
@@ -1660,6 +1668,8 @@ class Box2dPlugin
1660
1668
  const fixtureB = contact.GetFixtureB();
1661
1669
  const objectA = fixtureA.GetBody().object;
1662
1670
  const objectB = fixtureB.GetBody().object;
1671
+ // raw user-created b2Bodies may have no .object — skip those
1672
+ if (!objectA || !objectB) return;
1663
1673
  objectA.beginContact(objectB);
1664
1674
  objectB.beginContact(objectA);
1665
1675
  }
@@ -1670,6 +1680,7 @@ class Box2dPlugin
1670
1680
  const fixtureB = contact.GetFixtureB();
1671
1681
  const objectA = fixtureA.GetBody().object;
1672
1682
  const objectB = fixtureB.GetBody().object;
1683
+ if (!objectA || !objectB) return;
1673
1684
  objectA.endContact(objectB);
1674
1685
  objectB.endContact(objectA);
1675
1686
  };
@@ -2048,7 +2059,7 @@ async function box2dInit()
2048
2059
  debugDraw.DrawTransform = function(transform)
2049
2060
  {
2050
2061
  transform = box2d.instance.wrapPointer(transform, box2d.instance.b2Transform);
2051
- const pos = vec2(transform.get_p());
2062
+ const pos = box2d.vec2From(transform.get_p());
2052
2063
  const angle = -transform.get_q().GetAngle();
2053
2064
  const p1 = vec2(1,0), c1 = rgb(.75,0,0,.8);
2054
2065
  const p2 = vec2(0,1), c2 = rgb(0,.75,0,.8);
@@ -82,8 +82,20 @@ class NewgroundsPlugin
82
82
 
83
83
  // get medals
84
84
  const medalsResult = this.call('Medal.getList');
85
+
86
+ // bail early if the first call failed (offline / bad session /
87
+ // server error) so we don't block the main thread on more sync
88
+ // XHRs that are guaranteed to also fail
89
+ if (!medalsResult || !medalsResult.result || medalsResult.result.error)
90
+ {
91
+ debugMedals && LOG('Newgrounds session unavailable; skipping plugin init');
92
+ this.medals = [];
93
+ this.scoreboards = [];
94
+ return;
95
+ }
96
+
85
97
  /** @property {Array} - Medals fetched from Newgrounds (empty until session is active) */
86
- this.medals = medalsResult ? medalsResult.result.data['medals'] : [];
98
+ this.medals = medalsResult.result.data?.['medals'] || [];
87
99
  debugMedals && LOG(this.medals);
88
100
  for (const newgroundsMedal of this.medals)
89
101
  {
@@ -103,11 +115,11 @@ class NewgroundsPlugin
103
115
  medal.description = medal.description + ` (${ medal.value })`;
104
116
  }
105
117
  }
106
-
118
+
107
119
  // get scoreboards
108
120
  const scoreboardResult = this.call('ScoreBoard.getBoards');
109
121
  /** @property {Array} - Scoreboards fetched from Newgrounds */
110
- this.scoreboards = scoreboardResult ? scoreboardResult.result.data.scoreboards : [];
122
+ this.scoreboards = scoreboardResult?.result?.data?.scoreboards || [];
111
123
  debugMedals && LOG(this.scoreboards);
112
124
 
113
125
  // keep the session alive with a ping every minute
@@ -271,11 +271,13 @@ class PathFinder
271
271
  if (dx !== 0 && dy !== 0)
272
272
  {
273
273
  // Diagonal step: refuse if either cardinal neighbor is
274
- // blocked or has cost. Prevents cutting through corners.
274
+ // blocked. Prevents cutting through walls at corners.
275
+ // (Costed-but-walkable cardinals do not block — diagonal
276
+ // movement around expensive terrain is standard A*.)
275
277
  const card1 = this.getNode(current.pos.x + dx, current.pos.y);
276
- if (!card1 || card1.cost > 0 || !card1.walkable) continue;
278
+ if (!card1 || !card1.walkable) continue;
277
279
  const card2 = this.getNode(current.pos.x, current.pos.y + dy);
278
- if (!card2 || card2.cost > 0 || !card2.walkable) continue;
280
+ if (!card2 || !card2.walkable) continue;
279
281
  stepCost = PATHFINDER_DIAGONAL_COST;
280
282
  }
281
283
 
@@ -7,16 +7,16 @@ export
7
7
  // Medals
8
8
  medals,
9
9
  medalsPreventUnlock,
10
- medalsInit,
11
- medalsForEach,
12
- Medal,
13
10
  medalDisplayTime,
14
11
  medalDisplaySlideTime,
15
12
  medalDisplaySize,
13
+ medalsInit,
14
+ medalsForEach,
16
15
  setMedalDisplayTime,
17
16
  setMedalDisplaySlideTime,
18
17
  setMedalDisplaySize,
19
18
  setMedalsPreventUnlock,
19
+ Medal,
20
20
 
21
21
  // Newgrounds
22
22
  newgrounds,
@@ -29,6 +29,7 @@ export
29
29
 
30
30
  // ZzFXMusic
31
31
  ZzFXMusic,
32
+ zzfxM,
32
33
 
33
34
  // UI System
34
35
  uiSystem,
@@ -107,10 +107,14 @@ class PostProcessPlugin
107
107
  function postProcessRender()
108
108
  {
109
109
  if (headlessMode || !glEnable) return;
110
-
110
+
111
111
  // clear out the buffer
112
112
  glFlush();
113
113
 
114
+ // ensure we render to the default framebuffer (in case any earlier
115
+ // caller this frame left a render target bound)
116
+ glContext.bindFramebuffer(glContext.FRAMEBUFFER, null);
117
+
114
118
  // setup shader program to draw a quad
115
119
  glContext.useProgram(postProcess.shader);
116
120
  glContext.bindVertexArray(postProcess.vao);
@@ -151,6 +155,9 @@ class PostProcessPlugin
151
155
  glContext.texImage2D(glContext.TEXTURE_2D, 0, glContext.RGBA, glContext.RGBA, glContext.UNSIGNED_BYTE, glCanvas);
152
156
  }
153
157
 
158
+ // restore default so subsequent dynamic texture uploads aren't flipped
159
+ glContext.pixelStorei(glContext.UNPACK_FLIP_Y_WEBGL, false);
160
+
154
161
  // force it to set instanced mode
155
162
  glSetInstancedMode(true);
156
163
  }
@@ -258,7 +258,7 @@ const Ease =
258
258
  * @param {number} x
259
259
  * @returns {number}
260
260
  * @memberof TweenSystem */
261
- EXPO: (x) => 2 ** (10 * x - 10),
261
+ EXPO: (x) => x === 0 ? 0 : 2 ** (10 * x - 10),
262
262
 
263
263
  /** Back ease-in: overshoots backward at the start before snapping forward.
264
264
  * @param {number} x
@@ -271,6 +271,8 @@ const Ease =
271
271
  * @returns {number}
272
272
  * @memberof TweenSystem */
273
273
  ELASTIC: (x) =>
274
+ x === 0 ? 0 :
275
+ x === 1 ? 1 :
274
276
  -(2 ** (10 * x - 10)) * sin(((37 - 40 * x) * PI) / 6),
275
277
 
276
278
  /** Spring-like ease-out: oscillates outward after passing the target.
@@ -431,29 +433,32 @@ function tweenProperty(target, propertyPath, start, end, duration = 1, options =
431
433
  }
432
434
 
433
435
  // Continuation that schedules the next loop iteration when one finishes.
434
- // Called from the completed tween's `then` slot. Decrements the counter and
435
- // only spawns a new tween if more iterations remain.
436
- function loopContinuation(prev)
436
+ // Reuses the same Tween object across iterations so the user's handle
437
+ // from `.loop()` keeps working — calling `.stop()` mid-loop now cancels
438
+ // the entire chain instead of just the current iteration.
439
+ function loopContinuation(tween)
437
440
  {
438
- if (prev.loopRemaining !== Infinity && prev.loopRemaining <= 1) return;
439
- const next = new Tween(prev.callback, prev.start, prev.end, prev.duration,
440
- { ease: prev.ease, useRealTime: prev.useRealTime });
441
- next.loopRemaining = prev.loopRemaining === Infinity
442
- ? Infinity
443
- : prev.loopRemaining - 1;
444
- next.thenCallback = () => loopContinuation(next);
441
+ if (tween.loopRemaining !== Infinity && tween.loopRemaining <= 1) return;
442
+ if (tween.loopRemaining !== Infinity) tween.loopRemaining -= 1;
443
+ tween.life = tween.duration;
444
+ tween.thenCallback = () => loopContinuation(tween);
445
+ tweenActive.push(tween);
446
+ // snap to start for the new iteration (matches Tween constructor behavior)
447
+ tween.callback(tween.interp(tween.duration));
445
448
  }
446
449
 
447
- // Continuation for pingPong: spawns a new tween with start and end swapped.
448
- function pingPongContinuation(prev)
450
+ // Continuation for pingPong: swaps start and end on the same tween each iteration.
451
+ function pingPongContinuation(tween)
449
452
  {
450
- if (prev.loopRemaining !== Infinity && prev.loopRemaining <= 1) return;
451
- const next = new Tween(prev.callback, prev.end, prev.start, prev.duration,
452
- { ease: prev.ease, useRealTime: prev.useRealTime });
453
- next.loopRemaining = prev.loopRemaining === Infinity
454
- ? Infinity
455
- : prev.loopRemaining - 1;
456
- next.thenCallback = () => pingPongContinuation(next);
453
+ if (tween.loopRemaining !== Infinity && tween.loopRemaining <= 1) return;
454
+ if (tween.loopRemaining !== Infinity) tween.loopRemaining -= 1;
455
+ const tmp = tween.start;
456
+ tween.start = tween.end;
457
+ tween.end = tmp;
458
+ tween.life = tween.duration;
459
+ tween.thenCallback = () => pingPongContinuation(tween);
460
+ tweenActive.push(tween);
461
+ tween.callback(tween.interp(tween.duration));
457
462
  }
458
463
 
459
464
  /** Engine plugin hook: advance every active tween by the appropriate delta.
@@ -456,11 +456,17 @@ class UISystemPlugin
456
456
  * @param {DragAndDropCallback} [onDragOver] - continuously when dragging over */
457
457
  setupDragAndDrop(onDrop, onDragEnter, onDragLeave, onDragOver)
458
458
  {
459
- function setCallback(callback, listenerType)
459
+ // remove any prior listeners so repeated setup calls don't stack
460
+ if (this._dragListeners)
461
+ for (const [type, listener] of this._dragListeners)
462
+ document.removeEventListener(type, listener);
463
+ this._dragListeners = [];
464
+ const setCallback = (callback, listenerType)=>
460
465
  {
461
- function listener(e) { e.preventDefault(); callback && callback(e); }
466
+ const listener = (e)=> { e.preventDefault(); callback && callback(e); };
462
467
  document.addEventListener(listenerType, listener);
463
- }
468
+ this._dragListeners.push([listenerType, listener]);
469
+ };
464
470
  setCallback(onDrop, 'drop');
465
471
  setCallback(onDragEnter, 'dragenter');
466
472
  setCallback(onDragLeave, 'dragleave');
@@ -784,6 +790,15 @@ class UIObject
784
790
  if (this.destroyed)
785
791
  return;
786
792
 
793
+ // clear ui-system references that point at this object so events
794
+ // don't keep firing against a destroyed target (especially the
795
+ // keydown listener attached for keyInputObject)
796
+ if (uiSystem.activeObject === this) uiSystem.activeObject = undefined;
797
+ if (uiSystem.hoverObject === this) uiSystem.hoverObject = undefined;
798
+ if (uiSystem.lastHoverObject === this) uiSystem.lastHoverObject = undefined;
799
+ if (uiSystem.navigationObject === this) uiSystem.navigationObject = undefined;
800
+ if (uiSystem.keyInputObject === this) uiSystem.keyInputObject = undefined;
801
+
787
802
  // disconnect from parent and destroy children
788
803
  this.destroyed = 1;
789
804
  this.parent?.removeChild(this);
@@ -792,6 +807,8 @@ class UIObject
792
807
  child.parent = undefined;
793
808
  child.destroy();
794
809
  }
810
+ // clear references so destroyed children can be GC'd
811
+ this.children.length = 0;
795
812
  }
796
813
 
797
814
  /** Check if the mouse is overlapping this ui object
@@ -1381,6 +1398,7 @@ class UISlider extends UIObject
1381
1398
  {
1382
1399
  // toggle value between 0 and 1
1383
1400
  this.value = this.value ? 0 : 1;
1401
+ this.onChange();
1384
1402
  this.onRelease();
1385
1403
  super.navigatePressed();
1386
1404
  }
package/src/engine.js CHANGED
@@ -32,7 +32,7 @@ const engineName = 'LittleJS';
32
32
  * @type {string}
33
33
  * @default
34
34
  * @memberof Engine */
35
- const engineVersion = '1.18.12';
35
+ const engineVersion = '1.18.15';
36
36
 
37
37
  /** Frames per second to update
38
38
  * @type {number}
@@ -161,12 +161,20 @@ function engineAddPlugin(update, render, glContextLost, glContextRestored)
161
161
  * ['tiles.png', 'tilesLevel.png'] // images to load
162
162
  * );
163
163
  * @memberof Engine */
164
- async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRenderPost, imageSources=[], rootElement=document.body)
164
+ async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRenderPost, imageSources=[], rootElement)
165
165
  {
166
166
  showEngineVersion && console.log(`${engineName} Engine v${engineVersion}`);
167
167
  ASSERT(!mainContext, 'engine already initialized');
168
+ // runtime guard so release builds (where the assert is stripped) don't
169
+ // double-register listeners / double-add canvases on a second call
170
+ if (mainContext) return;
168
171
  ASSERT(isArray(imageSources), 'pass in images as array');
169
172
 
173
+ // ensure body exists for minimal HTML where the script runs before <body> is parsed
174
+ if (!document.body)
175
+ document.documentElement.appendChild(document.createElement('body'));
176
+ rootElement ||= document.body;
177
+
170
178
  // allow passing in empty functions
171
179
  gameInit ||= ()=>{};
172
180
  gameUpdate ||= ()=>{};
@@ -192,6 +200,9 @@ async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, game
192
200
  {
193
201
  // update time keeping
194
202
  let frameTimeDeltaMS = frameTimeMS - frameTimeLastMS;
203
+ // skip delta on the very first frame so timeReal doesn't jump
204
+ // by ~page-load-time when RAF starts handing real timestamps
205
+ if (!frameTimeLastMS) frameTimeDeltaMS = 0;
195
206
  frameTimeLastMS = frameTimeMS;
196
207
  if (debug || debugWatermark)
197
208
  averageFPS = lerp(averageFPS, 1e3/(frameTimeDeltaMS||1), .05);
@@ -422,7 +433,7 @@ async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, game
422
433
  promises.push(loadTexture(0));
423
434
 
424
435
  // load engine font image
425
- promises.push(fontImageInit());
436
+ promises.push(imageFontInit());
426
437
 
427
438
  if (showSplashScreen)
428
439
  {
@@ -103,7 +103,7 @@ class Sound
103
103
  /** @property {SoundLoadCallback} - function to call when sound is loaded */
104
104
  this.onloadCallback = onloadCallback;
105
105
 
106
- if (Array.isArray(asset))
106
+ if (isArray(asset))
107
107
  {
108
108
  // generate zzfx sound — copy so we don't mutate the caller's array
109
109
  const zzfxSound = asset.slice();
@@ -195,7 +195,7 @@ class Sound
195
195
  }
196
196
 
197
197
  /** Get how long this sound is in seconds
198
- * @return {number} - How long the sound is in seconds (undefined if loading)
198
+ * @return {number} - How long the sound is in seconds (0 if loading)
199
199
  */
200
200
  getDuration()
201
201
  { return this.sampleChannels?.[0]?.length / this.sampleRate || 0; }
@@ -352,10 +352,14 @@ class SoundInstance
352
352
  {
353
353
  if (fadeTime)
354
354
  {
355
- // ramp off gain
355
+ // ramp off gain from current volume (not 1, or low-volume
356
+ // instances would jump back up before fading);
357
+ // cancel any prior scheduling so stacked stop calls don't
358
+ // re-anchor partway through a previous fade
356
359
  const startFade = audioContext.currentTime;
357
360
  const endFade = startFade + fadeTime;
358
- this.gainNode.gain.linearRampToValueAtTime(1, startFade);
361
+ this.gainNode.gain.cancelScheduledValues(startFade);
362
+ this.gainNode.gain.setValueAtTime(this.volume, startFade);
359
363
  this.gainNode.gain.linearRampToValueAtTime(0, endFade);
360
364
  this.source.stop(endFade);
361
365
  }
@@ -403,13 +407,14 @@ class SoundInstance
403
407
  */
404
408
  getCurrentTime()
405
409
  {
406
- const deltaTime = mod(audioContext.currentTime - this.startTime,
407
- this.getDuration());
408
- return this.isPlaying() ? deltaTime : this.pausedTime;
410
+ if (!this.isPlaying()) return this.pausedTime;
411
+ const duration = this.getDuration();
412
+ // guard mod against 0 duration (rate=0 or sound not loaded)
413
+ return duration ? mod(audioContext.currentTime - this.startTime, duration) : 0;
409
414
  }
410
415
 
411
416
  /** Get the total duration of this sound
412
- * @return {number} - Total duration in seconds
417
+ * @return {number} - Total duration in seconds (0 if loading)
413
418
  */
414
419
  getDuration() { return this.rate ? this.sound.getDuration() / this.rate : 0; }
415
420
 
@@ -433,7 +438,7 @@ function speak(text, volume=1, rate=1, pitch=1, language='')
433
438
  {
434
439
  ASSERT(typeof volume !== 'string', 'speak() signature changed: language is now the last parameter, after pitch');
435
440
  if (!soundEnable || headlessMode) return;
436
- if (!speechSynthesis) return;
441
+ if (typeof speechSynthesis === 'undefined') return;
437
442
 
438
443
  // common languages (not supported by all browsers)
439
444
  // en - english, it - italian, fr - french, de - german, es - spanish
@@ -451,7 +456,11 @@ function speak(text, volume=1, rate=1, pitch=1, language='')
451
456
 
452
457
  /** Stop all queued speech
453
458
  * @memberof Audio */
454
- function speakStop() {speechSynthesis?.cancel();}
459
+ function speakStop()
460
+ {
461
+ if (typeof speechSynthesis !== 'undefined')
462
+ speechSynthesis.cancel();
463
+ }
455
464
 
456
465
  /** Get frequency of a note on a musical scale
457
466
  * @param {number} semitoneOffset - How many semitones away from the root note
@@ -513,9 +522,14 @@ function playSamples(sampleChannels, volume=1, rate=1, pan=0, loop=false, sample
513
522
  const pannerNode = new StereoPannerNode(audioContext, {'pan':clamp(pan, -1, 1)});
514
523
  source.connect(pannerNode).connect(gainNode);
515
524
 
516
- // callback when the sound ends
517
- if (onended)
518
- source.addEventListener('ended', ()=> onended(source));
525
+ // disconnect nodes when the sound ends so the audio graph doesn't grow
526
+ // unbounded across many play() calls (source.stop() also fires 'ended')
527
+ source.addEventListener('ended', ()=>
528
+ {
529
+ gainNode.disconnect();
530
+ pannerNode.disconnect();
531
+ if (onended) onended(source);
532
+ });
519
533
 
520
534
  // play and return sound
521
535
  const startOffset = offset * rate;
@@ -196,7 +196,7 @@ function closureCompilerStep(filename)
196
196
  fs.copyFileSync(filename, filenameTemp);
197
197
  try
198
198
  {
199
- execSync(`npx google-closure-compiler --js=${filenameTemp} --js_output_file=${filename} --warning_level=VERBOSE --jscomp_off=*`);
199
+ execSync(`npx google-closure-compiler --js=${filenameTemp} --js_output_file=${filename} --jscomp_off=*`);
200
200
  fs.rmSync(filenameTemp);
201
201
  }
202
202
  catch (e) { handleError(e, 'Failed to run Closure Compiler step!'); }
@@ -83,12 +83,8 @@ function debugRect(pos, size=vec2(), color=WHITE, time=0, angle=0, fill=false, s
83
83
  ASSERT(isNumber(time), 'time must be a number');
84
84
  ASSERT(isNumber(angle), 'angle must be a number');
85
85
 
86
- if (typeof size === 'number')
87
- size = vec2(size); // allow passing in floats
88
86
  if (isColor(color))
89
87
  color = color.toString();
90
- pos = pos.copy();
91
- size = size.copy();
92
88
  const timer = new Timer(time);
93
89
  debugPrimitives.push({pos:pos.copy(), size:size.copy(), color, timer, angle, fill, screenSpace});
94
90
  }
@@ -158,7 +154,7 @@ function debugPoint(pos, color, time, angle, screenSpace=false)
158
154
  * @param {number} [time]
159
155
  * @param {boolean} [screenSpace]
160
156
  * @memberof Debug */
161
- function debugLine(posA, posB, color, width=.1, time, screenSpace=false)
157
+ function debugLine(posA, posB, color, width=.1, time=0, screenSpace=false)
162
158
  {
163
159
  ASSERT(isVector2(posA), 'posA must be a vec2');
164
160
  ASSERT(isVector2(posB), 'posB must be a vec2');
@@ -196,7 +192,7 @@ function debugOverlap(posA, sizeA, posB, sizeB, color, time, screenSpace=false)
196
192
  debugRect(minPos.lerp(maxPos,.5), maxPos.subtract(minPos), color, time, 0, false, screenSpace);
197
193
  }
198
194
 
199
- /** Draw a debug axis aligned bounding box in world space
195
+ /** Draw debug text in world space
200
196
  * @param {string|number} text
201
197
  * @param {Vector2} pos
202
198
  * @param {number} [size]
@@ -527,7 +523,7 @@ function debugRender()
527
523
  continue;
528
524
  if (parseInt(i) < 3)
529
525
  mousePressed += i + ' ' ;
530
- else if (keyIsDown(i, 0))
526
+ else
531
527
  keysPressed += i + ' ' ;
532
528
  }
533
529
  mousePressed && debugContext.fillText('Mouse: ' + mousePressed, x, y += h);