littlejsengine 1.24.3 → 1.26.0

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 (53) hide show
  1. package/CHANGELOG.md +123 -0
  2. package/EDITOR.md +2 -2
  3. package/FAQ.md +55 -11
  4. package/README.md +6 -2
  5. package/REFERENCE.md +203 -57
  6. package/dist/littlejs.d.ts +933 -313
  7. package/dist/littlejs.esm.js +5143 -2419
  8. package/dist/littlejs.esm.min.js +38 -24
  9. package/dist/littlejs.js +5132 -2416
  10. package/dist/littlejs.min.js +37 -23
  11. package/dist/littlejs.release.js +4905 -2232
  12. package/package.json +4 -4
  13. package/plugins/box2d.js +141 -56
  14. package/plugins/drawUtilities.js +374 -380
  15. package/plugins/gltf.js +383 -60
  16. package/plugins/lightSystem.js +412 -30
  17. package/plugins/math3d.js +1015 -1014
  18. package/plugins/medalSystem.js +19 -7
  19. package/plugins/newgrounds.js +5 -6
  20. package/plugins/parallax.js +1 -0
  21. package/plugins/particleEffects.js +47 -14
  22. package/plugins/pathFinder.js +60 -38
  23. package/plugins/pluginExport.js +11 -0
  24. package/plugins/postProcess.js +200 -67
  25. package/plugins/render3d.js +493 -2391
  26. package/plugins/render3dDebug.js +5 -8
  27. package/plugins/render3dEditor.js +84 -9
  28. package/plugins/render3dExtras.js +122 -33
  29. package/plugins/render3dLevel.js +67 -8
  30. package/plugins/render3dMesh.js +1158 -0
  31. package/plugins/render3dObject.js +1451 -0
  32. package/plugins/render3dVoxels.js +15 -3
  33. package/plugins/textureSheet.js +9 -8
  34. package/plugins/threejs.js +1 -1
  35. package/plugins/tweenSystem.js +117 -87
  36. package/plugins/uiSystem.js +62 -28
  37. package/plugins/wavedash.js +229 -0
  38. package/src/engine.js +192 -40
  39. package/src/engineAudio.js +47 -27
  40. package/src/engineBuild.mjs +6 -1
  41. package/src/engineDebug.js +23 -162
  42. package/src/engineDraw.js +148 -43
  43. package/src/engineEditor.js +153 -40
  44. package/src/engineExport.js +0 -3
  45. package/src/engineInput.js +31 -25
  46. package/src/engineMath.js +38 -20
  47. package/src/engineObject.js +68 -50
  48. package/src/engineParticles.js +66 -18
  49. package/src/engineRelease.js +18 -15
  50. package/src/engineSettings.js +48 -9
  51. package/src/engineTileLayer.js +44 -18
  52. package/src/engineUtilities.js +56 -11
  53. package/src/engineWebGL.js +68 -22
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "littlejsengine",
3
- "version": "1.24.3",
3
+ "version": "1.26.0",
4
4
  "description": "LittleJS - Tiny and Fast HTML5 Game Engine",
5
5
  "main": "dist/littlejs.esm.js",
6
6
  "types": "dist/littlejs.d.ts",
@@ -11,6 +11,8 @@
11
11
  "default": "./dist/littlejs.esm.js"
12
12
  },
13
13
  "./dist/*": "./dist/*",
14
+ "./plugins/*": "./plugins/*",
15
+ "./src/*": "./src/*",
14
16
  "./package.json": "./package.json"
15
17
  },
16
18
  "repository": {
@@ -52,12 +54,10 @@
52
54
  "test:browser": "node test/browser/run.mjs"
53
55
  },
54
56
  "engines": {
55
- "node": "^20.19.0 || >=22.12.0"
57
+ "node": ">=22.12.0"
56
58
  },
57
59
  "devDependencies": {
58
60
  "bestzip": "^2.2.1",
59
- "electron": "^38.2.0",
60
- "electron-packager": "^17.1.2",
61
61
  "google-closure-compiler": "~20230502.0.0",
62
62
  "jsdoc": "~4.0.2",
63
63
  "roadroller": "~2.1.0",
package/plugins/box2d.js CHANGED
@@ -35,7 +35,7 @@ let box2dDebug = false;
35
35
  let box2dTempVectors;
36
36
  function box2dTemp(v, slot=0)
37
37
  {
38
- ASSERT(isVector2(v));
38
+ ASSERT(isVector2(v), 'Box2D: expected a vec2', v);
39
39
  const temp = (box2dTempVectors ||= [new box2d.instance.b2Vec2(), new box2d.instance.b2Vec2()])[slot];
40
40
  temp.Set(v.x, v.y);
41
41
  return temp;
@@ -88,6 +88,9 @@ function box2dWakeWithContacts(body)
88
88
  edge.get_other().SetAwake(true);
89
89
  }
90
90
 
91
+ // a number Box2D can take: it stops for good on NaN or Infinity, and a release build has no asserts to catch one first
92
+ const box2dFinite = (value, otherwise)=> isFinite(value) ? value : otherwise;
93
+
91
94
  // wake both bodies of a joint whose length, spring, ratio or strength changed, a sleeping body would stay where it was
92
95
  function box2dWakeJoint(joint)
93
96
  {
@@ -147,6 +150,64 @@ function box2dDestroyGears(joint)
147
150
  * @memberof Box2D */
148
151
  function box2dSetDebug(enable) { box2dDebug = enable; }
149
152
 
153
+ // the EngineObject physics fields a Box2D body does not read, each with what moves the body instead; a debug build
154
+ // warns once a field when a game sets one, which did nothing and said nothing
155
+ const box2dUnusedFields = {
156
+ velocity: 'setLinearVelocity', angleVelocity: 'setAngularVelocity', damping: 'setLinearDamping',
157
+ angleDamping: 'setAngularDamping', mass: 'setMass or the density of its shapes',
158
+ friction: 'the friction of its shapes, given to addBox and the rest',
159
+ restitution: 'the restitution of its shapes, given to addBox and the rest'};
160
+ const box2dUnusedWarned = new Set, box2dUnusedStarts = new WeakMap;
161
+
162
+ // the values of those fields as numbers, a vector as its two, then gravityScale, kept for the object
163
+ function box2dUnusedFieldValues(o)
164
+ {
165
+ const values = [];
166
+ for (const name in box2dUnusedFields)
167
+ {
168
+ const value = o[name];
169
+ value instanceof Vector2 ? values.push(value.x, value.y) : values.push(value);
170
+ }
171
+ values.push(o.gravityScale);
172
+ return values;
173
+ }
174
+
175
+ // warn once a field
176
+ function box2dUnusedWarn(name, use)
177
+ {
178
+ if (box2dUnusedWarned.has(name)) return;
179
+ box2dUnusedWarned.add(name);
180
+ console.warn('Box2dObject.' + name + ' does nothing on a Box2D body, use ' + use);
181
+ }
182
+
183
+ // warn once a field of one set on a Box2dObject, and of gravityScale set on the field, not with setGravityScale;
184
+ // compared in place, as it runs for every body every step of a debug build
185
+ function box2dCheckUnusedFields(o)
186
+ {
187
+ const start = box2dUnusedStarts.get(o);
188
+ if (!start) return;
189
+ let i = 0;
190
+ for (const name in box2dUnusedFields)
191
+ {
192
+ const value = o[name];
193
+ let changed;
194
+ if (value instanceof Vector2)
195
+ {
196
+ changed = value.x !== start[i] || value.y !== start[i + 1];
197
+ i += 2;
198
+ }
199
+ else
200
+ changed = value !== start[i++];
201
+ changed && box2dUnusedWarn(name, box2dUnusedFields[name]);
202
+ }
203
+
204
+ // the body's own scale is a call into Box2D, asked only when the field changed since it was last asked,
205
+ // since setGravityScale sets both
206
+ if (o.gravityScale === start[i]) return;
207
+ start[i] = o.gravityScale;
208
+ Math.fround(o.gravityScale) === o.body.GetGravityScale() || box2dUnusedWarn('gravityScale', 'setGravityScale');
209
+ }
210
+
150
211
  ///////////////////////////////////////////////////////////////////////////////
151
212
  /**
152
213
  * Box2D Object - extend with your own custom physics objects
@@ -154,6 +215,10 @@ function box2dSetDebug(enable) { box2dDebug = enable; }
154
215
  * - Provides interface for Box2D body and fixture functions
155
216
  * - Each object can have multiple fixtures and joints
156
217
  * - Angular values are clockwise like angle: angular velocity, torque, joint angles, limits and motor speeds
218
+ * - Box2D moves the body, so the EngineObject physics fields do nothing on it: velocity, angleVelocity, damping,
219
+ * angleDamping, mass, friction, restitution and gravityScale; use setLinearVelocity, setAngularVelocity,
220
+ * setLinearDamping, setAngularDamping, setMass, the friction and restitution of its shapes and setGravityScale.
221
+ * A debug build warns once when one of them is set
157
222
  * @extends EngineObject
158
223
  * @memberof Box2D
159
224
  */
@@ -196,6 +261,9 @@ class Box2dObject extends EngineObject
196
261
 
197
262
  this.body.object = this; // link body to this object
198
263
  box2d.objects.push(this); // keep track of all box2d objects
264
+
265
+ // a debug build notes the engine physics fields Box2D does not read, to warn when one is set
266
+ debug && box2dUnusedStarts.set(this, box2dUnusedFieldValues(this));
199
267
  }
200
268
 
201
269
  /** Destroy this object and its physics body
@@ -244,7 +312,7 @@ class Box2dObject extends EngineObject
244
312
  this.drawFixtures(this.color, this.lineColor, this.lineWidth);
245
313
  }
246
314
 
247
- /** Render debug info */
315
+ /** Draw its fixtures for the debug view: yellow asleep, blue static, both when both */
248
316
  renderDebugInfo()
249
317
  {
250
318
  const isAsleep = !this.getIsAwake();
@@ -253,7 +321,7 @@ class Box2dObject extends EngineObject
253
321
  this.drawFixtures(color);
254
322
  }
255
323
 
256
- /** Draws all this object's fixtures
324
+ /** Draw all this object's fixtures
257
325
  * @param {Color} [color]
258
326
  * @param {Color} [lineColor]
259
327
  * @param {number} [lineWidth]
@@ -303,7 +371,8 @@ class Box2dObject extends EngineObject
303
371
  * @param {number} [density]
304
372
  * @param {number} [friction]
305
373
  * @param {number} [restitution]
306
- * @param {boolean} [isSensor] */
374
+ * @param {boolean} [isSensor]
375
+ * @return {Object} - The fixture made, Box2D's own */
307
376
  addShape(shape, density=1, friction=.2, restitution=0, isSensor=false)
308
377
  {
309
378
  ASSERT(isNumber(density), 'density must be a number');
@@ -332,7 +401,8 @@ class Box2dObject extends EngineObject
332
401
  * @param {number} [density]
333
402
  * @param {number} [friction]
334
403
  * @param {number} [restitution]
335
- * @param {boolean} [isSensor] */
404
+ * @param {boolean} [isSensor]
405
+ * @return {Object|undefined} - The fixture made, Box2D's own, undefined for a shape too small to make */
336
406
  addBox(size=vec2(1), offset=vec2(), angle=0, density, friction, restitution, isSensor)
337
407
  {
338
408
  ASSERT(isVector2(size), 'size must be a Vector2');
@@ -341,8 +411,8 @@ class Box2dObject extends EngineObject
341
411
  ASSERT(isNumber(angle), 'angle must be a number');
342
412
 
343
413
  // Box2D stops for good on a box with almost no area, like addPoly no fixture is made from one
344
- ASSERT(size.x * size.y > 1e-6, 'box is too small for Box2D');
345
- if (!(size.x * size.y > 1e-6)) return;
414
+ ASSERT(size.x * size.y > 1e-6 && isFinite(size.x * size.y), 'box is too small or too big for Box2D');
415
+ if (!(size.x * size.y > 1e-6) || !isFinite(size.x * size.y)) return;
346
416
 
347
417
  const shape = new box2d.instance.b2PolygonShape();
348
418
  shape.SetAsBox(size.x/2, size.y/2, box2dTemp(offset), -angle);
@@ -357,7 +427,8 @@ class Box2dObject extends EngineObject
357
427
  * @param {number} [density]
358
428
  * @param {number} [friction]
359
429
  * @param {number} [restitution]
360
- * @param {boolean} [isSensor] */
430
+ * @param {boolean} [isSensor]
431
+ * @return {Object|undefined} - The fixture made, Box2D's own, undefined for a shape too small to make */
361
432
  addPoly(points, density, friction, restitution, isSensor)
362
433
  {
363
434
  ASSERT(isArray(points), 'points must be an array');
@@ -404,7 +475,8 @@ class Box2dObject extends EngineObject
404
475
  * @param {number} [density]
405
476
  * @param {number} [friction]
406
477
  * @param {number} [restitution]
407
- * @param {boolean} [isSensor] */
478
+ * @param {boolean} [isSensor]
479
+ * @return {Object|undefined} - The fixture made, Box2D's own, undefined for a shape too small to make */
408
480
  addRegularPoly(diameter=1, sides=8, density, friction, restitution, isSensor)
409
481
  {
410
482
  ASSERT(isNumber(diameter) && diameter>0, 'diameter must be a positive number');
@@ -424,7 +496,8 @@ class Box2dObject extends EngineObject
424
496
  * @param {number} [density]
425
497
  * @param {number} [friction]
426
498
  * @param {number} [restitution]
427
- * @param {boolean} [isSensor] */
499
+ * @param {boolean} [isSensor]
500
+ * @return {Object|undefined} - The fixture made, Box2D's own, undefined for a shape too small to make */
428
501
  addRandomPoly(diameter=1, density, friction, restitution, isSensor)
429
502
  {
430
503
  ASSERT(isNumber(diameter) && diameter>0, 'diameter must be a positive number');
@@ -443,12 +516,14 @@ class Box2dObject extends EngineObject
443
516
  * @param {number} [density]
444
517
  * @param {number} [friction]
445
518
  * @param {number} [restitution]
446
- * @param {boolean} [isSensor] */
519
+ * @param {boolean} [isSensor]
520
+ * @return {Object|undefined} - The fixture made, Box2D's own, undefined for a circle too big to make */
447
521
  addCircle(diameter=1, offset=vec2(), density, friction, restitution, isSensor)
448
522
  {
449
- ASSERT(isNumber(diameter) && diameter>0, 'diameter must be a positive number');
523
+ ASSERT(isNumber(diameter) && diameter>0 && isFinite(diameter), 'diameter must be a positive number');
450
524
  ASSERT(isVector2(offset), 'offset must be a Vector2');
451
-
525
+ if (!isFinite(diameter)) return; // Box2D stops for good on it, no fixture is made
526
+
452
527
  const shape = new box2d.instance.b2CircleShape();
453
528
  shape.set_m_p(box2dTemp(offset));
454
529
  shape.set_m_radius(diameter/2);
@@ -463,7 +538,8 @@ class Box2dObject extends EngineObject
463
538
  * @param {number} [density]
464
539
  * @param {number} [friction]
465
540
  * @param {number} [restitution]
466
- * @param {boolean} [isSensor] */
541
+ * @param {boolean} [isSensor]
542
+ * @return {Object} - The fixture made, Box2D's own */
467
543
  addEdge(point1, point2, density, friction, restitution, isSensor)
468
544
  {
469
545
  ASSERT(isVector2(point1), 'point1 must be a Vector2');
@@ -481,7 +557,8 @@ class Box2dObject extends EngineObject
481
557
  * @param {number} [density]
482
558
  * @param {number} [friction]
483
559
  * @param {number} [restitution]
484
- * @param {boolean} [isSensor] */
560
+ * @param {boolean} [isSensor]
561
+ * @return {Array<Object>} - The fixtures made, Box2D's own */
485
562
  addEdgeList(points, density, friction, restitution, isSensor)
486
563
  {
487
564
  ASSERT(isArray(points), 'points must be an array');
@@ -514,7 +591,8 @@ class Box2dObject extends EngineObject
514
591
  * @param {number} [density]
515
592
  * @param {number} [friction]
516
593
  * @param {number} [restitution]
517
- * @param {boolean} [isSensor] */
594
+ * @param {boolean} [isSensor]
595
+ * @return {Array<Object>} - The fixtures made, Box2D's own */
518
596
  addEdgeLoop(points, density, friction, restitution, isSensor)
519
597
  {
520
598
  ASSERT(isArray(points), 'points must be an array');
@@ -676,12 +754,14 @@ class Box2dObject extends EngineObject
676
754
  setAngularVelocity(angularVelocity)
677
755
  { this.body.SetAngularVelocity(-angularVelocity); }
678
756
 
679
- /** Sets the linear damping
757
+ /** Sets the linear damping, Box2D's: a rate, 0 none and larger slows it faster, with no upper limit, where the
758
+ * damping of an EngineObject is the fraction it keeps each frame and does nothing on a Box2dObject
680
759
  * @param {number} damping */
681
760
  setLinearDamping(damping)
682
761
  { this.body.SetLinearDamping(damping); }
683
762
 
684
- /** Sets the angular damping
763
+ /** Sets the angular damping, Box2D's: a rate, 0 none and larger slows its turning faster, with no upper limit;
764
+ * angleDamping does nothing on a Box2dObject
685
765
  * @param {number} damping */
686
766
  setAngularDamping(damping)
687
767
  { this.body.SetAngularDamping(damping); }
@@ -758,7 +838,7 @@ class Box2dObject extends EngineObject
758
838
  const f = Math.fround;
759
839
  const oldInertia = f(data.get_I() - box2dCenterInertia(oldMass, center.get_x(), center.get_y()));
760
840
  const inertia = momentOfInertia ?? oldInertia;
761
- mass ??= oldMass;
841
+ mass = box2dFinite(mass ?? oldMass, oldMass);
762
842
  const offset = box2dCenterInertia(mass > 0 ? mass : 1, cx, cy); // a mass of 0 or less is 1 to Box2D
763
843
  const I = f(inertia + offset);
764
844
  data.set_mass(mass);
@@ -1079,13 +1159,15 @@ class Box2dJoint
1079
1159
  const bodyA = jointDef.get_bodyA(), bodyB = jointDef.get_bodyB();
1080
1160
  const bothLive = !box2d.isNull(bodyA) && !box2d.isNull(bodyB);
1081
1161
  ASSERT(bothLive, 'a joint needs two objects that are not destroyed');
1082
- ASSERT(box2d.instance.getPointer(bodyA) !== box2d.instance.getPointer(bodyB), 'a joint needs two different objects');
1162
+ const twoObjects = !bothLive || box2d.instance.getPointer(bodyA) !== box2d.instance.getPointer(bodyB);
1163
+ ASSERT(twoObjects, 'a joint needs two different objects');
1083
1164
 
1084
1165
  /** @property {Object} - The Box2d joint, 0 once it is destroyed, as it is when either object is */
1085
1166
  this.box2dJoint = 0;
1086
- if (!bothLive)
1167
+ if (!bothLive || !twoObjects)
1087
1168
  {
1088
- // one of its objects is gone, so it is made destroyed, as it would be had it gone after
1169
+ // one of its objects is gone, so it is made destroyed, as it would be had it gone after; a joint of an
1170
+ // object to itself, which Box2D stops for good on, is made destroyed too in a release build
1089
1171
  box2d.instance.destroy(jointDef);
1090
1172
  return;
1091
1173
  }
@@ -1197,7 +1279,7 @@ class Box2dTargetJoint extends Box2dJoint
1197
1279
 
1198
1280
  /** Sets the joint frequency in Hertz, above 0, Box2D stops for good on 0
1199
1281
  * @param {number} hz */
1200
- setFrequency(hz) { this.box2dJoint.SetFrequency(max(hz, 1e-3)); box2dWakeJoint(this.box2dJoint); }
1282
+ setFrequency(hz) { this.box2dJoint.SetFrequency(max(box2dFinite(hz, 0), 1e-3)); box2dWakeJoint(this.box2dJoint); }
1201
1283
 
1202
1284
  /** Gets the joint frequency in Hertz
1203
1285
  * @return {number} */
@@ -1260,11 +1342,11 @@ class Box2dDistanceJoint extends Box2dJoint
1260
1342
  * @return {number} */
1261
1343
  getFrequency() { return this.box2dJoint.GetFrequency(); }
1262
1344
 
1263
- /** Set the damping ratio
1345
+ /** Set how much the spring is damped, 0 for none and 1 to stop it bouncing
1264
1346
  * @param {number} ratio */
1265
1347
  setDampingRatio(ratio) { this.box2dJoint.SetDampingRatio(ratio); box2dWakeJoint(this.box2dJoint); }
1266
1348
 
1267
- /** Get the damping ratio
1349
+ /** Get how much the spring is damped, 0 for none and 1 to stop it bouncing
1268
1350
  * @return {number} */
1269
1351
  getDampingRatio() { return this.box2dJoint.GetDampingRatio(); }
1270
1352
  }
@@ -1393,6 +1475,7 @@ class Box2dRevoluteJoint extends Box2dJoint
1393
1475
  setLimits(min, max)
1394
1476
  {
1395
1477
  ASSERT(min <= max, 'the lower limit must not be above the upper one');
1478
+ min = box2dFinite(min, 0), max = box2dFinite(max, 0); // or on NaN, which passes the order check
1396
1479
  if (min > max) [min, max] = [max, min]; // Box2D stops on them reversed
1397
1480
  this.box2dJoint.SetLimits(-max, -min);
1398
1481
  }
@@ -1417,7 +1500,7 @@ class Box2dRevoluteJoint extends Box2dJoint
1417
1500
  * @param {number} torque */
1418
1501
  setMaxMotorTorque(torque) { this.box2dJoint.SetMaxMotorTorque(torque); }
1419
1502
 
1420
- /** Get the max motor torque
1503
+ /** Get the most torque the motor can apply, a magnitude
1421
1504
  * @return {number} */
1422
1505
  getMaxMotorTorque() { return this.box2dJoint.GetMaxMotorTorque(); }
1423
1506
 
@@ -1432,6 +1515,8 @@ class Box2dRevoluteJoint extends Box2dJoint
1432
1515
  * Box2D Pin Joint
1433
1516
  * - Pins two objects together at a point, where they still turn freely, like a nail through two boards
1434
1517
  * - A revolute joint at that point, so it holds exactly and its limits and motor work too
1518
+ * - new Box2dPinJoint(objectA, objectB, pos, collide): the point defaults to objectA's position, where a revolute
1519
+ * joint's anchor defaults to objectB's
1435
1520
  * @extends Box2dRevoluteJoint
1436
1521
  * @memberof Box2D
1437
1522
  */
@@ -1593,6 +1678,7 @@ class Box2dPrismaticJoint extends Box2dJoint
1593
1678
  setLimits(min, max)
1594
1679
  {
1595
1680
  ASSERT(min <= max, 'the lower limit must not be above the upper one');
1681
+ min = box2dFinite(min, 0), max = box2dFinite(max, 0); // or on NaN, which passes the order check
1596
1682
  if (min > max) [min, max] = [max, min]; // Box2D stops on them reversed
1597
1683
  this.box2dJoint.SetLimits(min, max);
1598
1684
  }
@@ -1605,11 +1691,11 @@ class Box2dPrismaticJoint extends Box2dJoint
1605
1691
  * @param {boolean} [enable] */
1606
1692
  enableMotor(enable=true) { this.box2dJoint.EnableMotor(enable); }
1607
1693
 
1608
- /** Set the motor speed
1694
+ /** Set the speed the motor drives the bodies apart along the axis, in meters per second
1609
1695
  * @param {number} speed */
1610
1696
  setMotorSpeed(speed) { this.box2dJoint.SetMotorSpeed(speed); }
1611
1697
 
1612
- /** Get the motor speed
1698
+ /** Get the speed the motor drives the bodies apart along the axis, in meters per second
1613
1699
  * @return {number} */
1614
1700
  getMotorSpeed() { return this.box2dJoint.GetMotorSpeed(); }
1615
1701
 
@@ -1701,7 +1787,7 @@ class Box2dWheelJoint extends Box2dJoint
1701
1787
  * @param {number} torque */
1702
1788
  setMaxMotorTorque(torque) { this.box2dJoint.SetMaxMotorTorque(torque); }
1703
1789
 
1704
- /** Get the max motor torque
1790
+ /** Get the most torque the wheel's motor can apply, a magnitude
1705
1791
  * @return {number} */
1706
1792
  getMaxMotorTorque() { return this.box2dJoint.GetMaxMotorTorque(); }
1707
1793
 
@@ -1718,11 +1804,11 @@ class Box2dWheelJoint extends Box2dJoint
1718
1804
  * @return {number} */
1719
1805
  getSpringFrequencyHz() { return this.box2dJoint.GetSpringFrequencyHz(); }
1720
1806
 
1721
- /** Set the spring damping ratio
1807
+ /** Set how much the suspension spring is damped, 0 for none and 1 to stop it bouncing
1722
1808
  * @param {number} ratio */
1723
1809
  setSpringDampingRatio(ratio) { this.box2dJoint.SetSpringDampingRatio(ratio); box2dWakeJoint(this.box2dJoint); }
1724
1810
 
1725
- /** Get the spring damping ratio
1811
+ /** Get how much the suspension spring is damped, 0 for none and 1 to stop it bouncing
1726
1812
  * @return {number} */
1727
1813
  getSpringDampingRatio() { return this.box2dJoint.GetSpringDampingRatio(); }
1728
1814
  }
@@ -1780,21 +1866,13 @@ class Box2dWeldJoint extends Box2dJoint
1780
1866
  * @return {number} */
1781
1867
  getFrequency() { return this.box2dJoint.GetFrequency(); }
1782
1868
 
1783
- /** Set the damping ratio
1869
+ /** Set how much the weld's spring is damped, 0 for none and 1 to stop it bouncing
1784
1870
  * @param {number} ratio */
1785
1871
  setDampingRatio(ratio) { this.box2dJoint.SetDampingRatio(ratio); box2dWakeJoint(this.box2dJoint); }
1786
1872
 
1787
- /** Get the damping ratio
1873
+ /** Get how much the weld's spring is damped, 0 for none and 1 to stop it bouncing
1788
1874
  * @return {number} */
1789
1875
  getDampingRatio() { return this.box2dJoint.GetDampingRatio(); }
1790
-
1791
- /** @deprecated since 1.20, use setDampingRatio
1792
- * @param {number} ratio */
1793
- setSpringDampingRatio(ratio) { this.setDampingRatio(ratio); }
1794
-
1795
- /** @deprecated since 1.20, use getDampingRatio
1796
- * @return {number} */
1797
- getSpringDampingRatio() { return this.getDampingRatio(); }
1798
1876
  }
1799
1877
 
1800
1878
  ///////////////////////////////////////////////////////////////////////////////
@@ -1836,7 +1914,7 @@ class Box2dFrictionJoint extends Box2dJoint
1836
1914
 
1837
1915
  /** Set the maximum friction force
1838
1916
  * @param {number} force */
1839
- setMaxForce(force) { this.box2dJoint.SetMaxForce(max(force, 0)); } // Box2D stops on a negative one
1917
+ setMaxForce(force) { this.box2dJoint.SetMaxForce(max(box2dFinite(force, 0), 0)); } // Box2D stops on a negative one
1840
1918
 
1841
1919
  /** Get the maximum friction force
1842
1920
  * @return {number} */
@@ -1844,7 +1922,7 @@ class Box2dFrictionJoint extends Box2dJoint
1844
1922
 
1845
1923
  /** Set the maximum friction torque
1846
1924
  * @param {number} torque */
1847
- setMaxTorque(torque) { this.box2dJoint.SetMaxTorque(max(torque, 0)); } // Box2D stops on a negative one
1925
+ setMaxTorque(torque) { this.box2dJoint.SetMaxTorque(max(box2dFinite(torque, 0), 0)); } // Box2D stops on a negative one
1848
1926
 
1849
1927
  /** Get the maximum friction torque
1850
1928
  * @return {number} */
@@ -1885,7 +1963,7 @@ class Box2dPulleyJoint extends Box2dJoint
1885
1963
  jointDef.set_localAnchorA(box2dTemp(localAnchorA));
1886
1964
  jointDef.set_localAnchorB(box2dTemp(localAnchorB));
1887
1965
  ASSERT(ratio, 'a pulley ratio can not be 0');
1888
- jointDef.set_ratio(ratio);
1966
+ jointDef.set_ratio(box2dFinite(ratio, 1) || 1); // Box2D stops for good on 0
1889
1967
  jointDef.set_lengthA(groundAnchorA.distance(anchorA));
1890
1968
  jointDef.set_lengthB(groundAnchorB.distance(anchorB));
1891
1969
  jointDef.set_collideConnected(collide);
@@ -1946,11 +2024,11 @@ class Box2dMotorJoint extends Box2dJoint
1946
2024
  super(jointDef);
1947
2025
  }
1948
2026
 
1949
- /** Set the target linear offset, in frame A, in meters.
2027
+ /** Set the target linear offset, in frame A, in meters
1950
2028
  * @param {Vector2} offset */
1951
2029
  setLinearOffset(offset) { this.box2dJoint.SetLinearOffset(box2dTemp(offset)); }
1952
2030
 
1953
- /** Get the target linear offset, in frame A, in meters.
2031
+ /** Get the target linear offset, in frame A, in meters
1954
2032
  * @return {Vector2} */
1955
2033
  getLinearOffset() { return box2d.vec2From(this.box2dJoint.GetLinearOffset()); }
1956
2034
 
@@ -1964,7 +2042,7 @@ class Box2dMotorJoint extends Box2dJoint
1964
2042
 
1965
2043
  /** Set the maximum force
1966
2044
  * @param {number} force */
1967
- setMaxForce(force) { this.box2dJoint.SetMaxForce(max(force, 0)); box2dWakeJoint(this.box2dJoint); } // Box2D stops on a negative one
2045
+ setMaxForce(force) { this.box2dJoint.SetMaxForce(max(box2dFinite(force, 0), 0)); box2dWakeJoint(this.box2dJoint); } // Box2D stops on a negative one
1968
2046
 
1969
2047
  /** Get the maximum force
1970
2048
  * @return {number} */
@@ -1972,7 +2050,7 @@ class Box2dMotorJoint extends Box2dJoint
1972
2050
 
1973
2051
  /** Set the maximum torque
1974
2052
  * @param {number} torque */
1975
- setMaxTorque(torque) { this.box2dJoint.SetMaxTorque(max(torque, 0)); box2dWakeJoint(this.box2dJoint); } // Box2D stops on a negative one
2053
+ setMaxTorque(torque) { this.box2dJoint.SetMaxTorque(max(box2dFinite(torque, 0), 0)); box2dWakeJoint(this.box2dJoint); } // Box2D stops on a negative one
1976
2054
 
1977
2055
  /** Get the maximum torque
1978
2056
  * @return {number} */
@@ -2322,15 +2400,17 @@ class Box2dPlugin
2322
2400
  // helper functions
2323
2401
 
2324
2402
  /** converts a box2d vec2 to a Vector2
2325
- * @param {Object} v */
2403
+ * @param {Object} v
2404
+ * @return {Vector2} */
2326
2405
  vec2From(v)
2327
2406
  {
2328
- ASSERT(v instanceof box2d.instance.b2Vec2);
2407
+ ASSERT(v instanceof box2d.instance.b2Vec2, 'vec2From: expected a b2Vec2', v);
2329
2408
  return new Vector2(v.get_x(), v.get_y());
2330
2409
  }
2331
2410
 
2332
2411
  /** converts a box2d vec2 pointer to a Vector2
2333
- * @param {Object} vp */
2412
+ * @param {Object} vp
2413
+ * @return {Vector2} */
2334
2414
  vec2FromPointer(vp)
2335
2415
  {
2336
2416
  const v = box2d.instance.wrapPointer(vp, box2d.instance.b2Vec2);
@@ -2339,19 +2419,22 @@ class Box2dPlugin
2339
2419
 
2340
2420
  /** converts a Vector2 to a new box2d vec2, which stays until destroyed with box2d.instance.destroy;
2341
2421
  * the plugin itself passes Box2D reused ones, since Box2D copies every vector it is given
2342
- * @param {Vector2} v */
2422
+ * @param {Vector2} v
2423
+ * @return {Object} - A Box2D vector, its b2Vec2 */
2343
2424
  vec2dTo(v)
2344
2425
  {
2345
- ASSERT(isVector2(v));
2426
+ ASSERT(isVector2(v), 'vec2dTo: expected a vec2', v);
2346
2427
  return new box2d.instance.b2Vec2(v.x, v.y);
2347
2428
  }
2348
2429
 
2349
2430
  /** checks if a box2d object is null
2350
- * @param {Object} o */
2431
+ * @param {Object} o
2432
+ * @return {boolean} */
2351
2433
  isNull(o) { return !box2d.instance.getPointer(o); }
2352
2434
 
2353
2435
  /** casts a box2d object to a shape type
2354
- * @param {Object} o */
2436
+ * @param {Object} o
2437
+ * @return {Object} - The shape as its own Box2D type */
2355
2438
  castShapeObject(o)
2356
2439
  {
2357
2440
  switch (o.GetType())
@@ -2370,7 +2453,8 @@ class Box2dPlugin
2370
2453
  }
2371
2454
 
2372
2455
  /** casts a box2d object to a joint type
2373
- * @param {Object} o */
2456
+ * @param {Object} o
2457
+ * @return {Object} - The joint as its own Box2D type */
2374
2458
  castJointObject(o)
2375
2459
  {
2376
2460
  switch (o.GetType())
@@ -2436,6 +2520,7 @@ async function box2dInit()
2436
2520
  {
2437
2521
  if (o.body)
2438
2522
  {
2523
+ debug && box2dCheckUnusedFields(o);
2439
2524
  // moved in place, as the engine moves an EngineObject's pos, so what holds it follows the body;
2440
2525
  // box2d uses reverse angle
2441
2526
  const p = o.body.GetPosition();