littlejsengine 1.19.3 → 1.21.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 (54) hide show
  1. package/FAQ.md +841 -820
  2. package/README.md +29 -25
  3. package/dist/littlejs.d.ts +2857 -955
  4. package/dist/littlejs.esm.js +33428 -20995
  5. package/dist/littlejs.esm.min.js +4 -3
  6. package/dist/littlejs.js +33386 -20996
  7. package/dist/littlejs.min.js +2 -2
  8. package/dist/littlejs.release.js +27841 -20422
  9. package/package.json +10 -5
  10. package/plugins/audioEffects.js +28 -24
  11. package/plugins/box2d.js +2516 -2073
  12. package/plugins/drawUtilities.js +206 -31
  13. package/plugins/gltf.js +786 -0
  14. package/plugins/jsconfig.json +19 -0
  15. package/plugins/lightSystem.js +719 -332
  16. package/plugins/math3d.js +148 -27
  17. package/plugins/medalSystem.js +326 -280
  18. package/plugins/newgrounds.js +400 -199
  19. package/plugins/pathFinder.js +208 -175
  20. package/plugins/pluginExport.js +25 -0
  21. package/plugins/postProcess.js +96 -24
  22. package/plugins/render3d.js +2067 -1632
  23. package/plugins/render3dDebug.js +110 -0
  24. package/plugins/render3dEditor.js +1812 -0
  25. package/plugins/render3dExtras.js +1948 -0
  26. package/plugins/render3dLevel.js +189 -0
  27. package/plugins/render3dVoxels.js +514 -0
  28. package/plugins/sceneSystem.js +58 -0
  29. package/plugins/textureSheet.js +496 -458
  30. package/plugins/threejs.js +183 -163
  31. package/plugins/tweakables.js +411 -0
  32. package/plugins/tweenSystem.js +265 -145
  33. package/plugins/uiSystem.js +2024 -1687
  34. package/plugins/zzfxm.js +159 -159
  35. package/src/engine.js +378 -97
  36. package/src/engineAudio.js +231 -127
  37. package/src/engineBuild.mjs +292 -256
  38. package/src/engineDebug.js +967 -743
  39. package/src/engineDraw.js +391 -198
  40. package/src/engineEditor.js +2452 -0
  41. package/src/engineExport.js +18 -0
  42. package/src/engineInput.js +1466 -1293
  43. package/src/engineLogo.js +19 -0
  44. package/src/engineMath.js +1274 -1252
  45. package/src/engineObject.js +197 -82
  46. package/src/engineParticles.js +583 -532
  47. package/src/engineRelease.js +79 -45
  48. package/src/engineSettings.js +115 -29
  49. package/src/engineTileLayer.js +995 -741
  50. package/src/engineUtilities.js +19 -13
  51. package/src/engineWebGL.js +239 -82
  52. package/src/jsconfig.json +17 -13
  53. package/plugins/box2d.wasm.js +0 -630
  54. package/plugins/box2d.wasm.wasm +0 -0
package/package.json CHANGED
@@ -1,13 +1,17 @@
1
1
  {
2
2
  "name": "littlejsengine",
3
- "version": "1.19.3",
3
+ "version": "1.21.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",
7
7
  "exports": {
8
- "types": "./dist/littlejs.d.ts",
9
- "production": "./dist/littlejs.esm.min.js",
10
- "default": "./dist/littlejs.esm.js"
8
+ ".": {
9
+ "types": "./dist/littlejs.d.ts",
10
+ "production": "./dist/littlejs.esm.min.js",
11
+ "default": "./dist/littlejs.esm.js"
12
+ },
13
+ "./dist/*": "./dist/*",
14
+ "./package.json": "./package.json"
11
15
  },
12
16
  "repository": {
13
17
  "type": "git",
@@ -33,6 +37,7 @@
33
37
  "dist/",
34
38
  "src/",
35
39
  "plugins/",
40
+ "!plugins/box2d.wasm.*",
36
41
  "COPYRIGHT.txt",
37
42
  "FAQ.md"
38
43
  ],
@@ -43,7 +48,7 @@
43
48
  "test": "node --test --import ./test/setup.mjs \"test/**/*.test.mjs\""
44
49
  },
45
50
  "engines": {
46
- "node": ">=18"
51
+ "node": "^20.19.0 || >=22.12.0"
47
52
  },
48
53
  "devDependencies": {
49
54
  "bestzip": "^2.2.1",
@@ -10,23 +10,7 @@
10
10
  'use strict';
11
11
 
12
12
  ///////////////////////////////////////////////////////////////////////////////
13
-
14
- // ramp an audio param to a value, cancelling anything already scheduled so stacked calls don't fight
15
- function audioParamRamp(param, value, fadeTime=0)
16
- {
17
- ASSERT(fadeTime >= 0, 'fadeTime must be positive or zero');
18
- const startTime = audioContext.currentTime;
19
- param.cancelScheduledValues(startTime);
20
- if (fadeTime)
21
- {
22
- param.setValueAtTime(param.value, startTime);
23
- param.linearRampToValueAtTime(value, startTime + fadeTime);
24
- }
25
- else
26
- param.value = value;
27
- }
28
-
29
- ///////////////////////////////////////////////////////////////////////////////
13
+ // audioParamRamp lives in engineAudio.js, core loads before plugins in the bundle
30
14
  /**
31
15
  * Base class for audio effects, an input and output with a wet/dry mix between them
32
16
  * - Sounds connect to input, output goes to the master gain until connect() moves it
@@ -44,6 +28,10 @@ class AudioEffect
44
28
  {
45
29
  ASSERT(isNumber(mix), 'mix must be a number');
46
30
 
31
+ /** @property {number} - Wet/dry balance, 0 is fully dry and 1 is fully wet */
32
+ this.mix = clamp(mix);
33
+ if (!audioContext) return; // no audio outside a browser, where the engine runs headless
34
+
47
35
  /** @property {GainNode} - Connect sounds to this node */
48
36
  this.input = audioContext.createGain();
49
37
  /** @property {GainNode} - This node carries the mixed result, send it somewhere with connect(), never by assigning here
@@ -53,8 +41,6 @@ class AudioEffect
53
41
  this.dryGain = audioContext.createGain();
54
42
  /** @property {GainNode} - Level of the processed signal */
55
43
  this.wetGain = audioContext.createGain();
56
- /** @property {number} - Wet/dry balance, 0 is fully dry and 1 is fully wet */
57
- this.mix = mix;
58
44
 
59
45
  this.input.connect(this.dryGain).connect(this.output);
60
46
  this.wetGain.connect(this.output);
@@ -71,6 +57,7 @@ class AudioEffect
71
57
  {
72
58
  ASSERT(isNumber(mix), 'mix must be a number');
73
59
  this.mix = mix = clamp(mix);
60
+ if (!audioContext) return;
74
61
  this.rampParam(this.dryGain.gain, 1-mix, fadeTime);
75
62
  this.rampParam(this.wetGain.gain, mix, fadeTime);
76
63
  }
@@ -94,10 +81,13 @@ class AudioEffect
94
81
  }
95
82
 
96
83
  /** Send this effect's output into another effect or audio node instead of the speakers
97
- * @param {AudioEffect|AudioNode} target - The next effect in the chain, or any audio node
98
- * @return {AudioEffect|AudioNode} - The target, so chains read left to right */
84
+ * @template {AudioEffect|AudioNode} T
85
+ * @param {T} target - The next effect in the chain, or any audio node
86
+ * @return {T} - The target, so chains read left to right */
99
87
  connect(target)
100
88
  {
89
+ if (!audioContext) return target; // no audio outside a browser, where the engine runs headless
90
+
101
91
  // an effect stands in for its input node, the same rule as sound.output
102
92
  const node = /** @type {AudioNode} */ (target && 'input' in target ? target.input : target);
103
93
  ASSERT(node && typeof node.connect === 'function', 'target must be an AudioEffect or AudioNode');
@@ -107,7 +97,7 @@ class AudioEffect
107
97
  }
108
98
 
109
99
  /** Stop sending this effect's output anywhere */
110
- disconnect() { this.output.disconnect(); }
100
+ disconnect() { this.output?.disconnect(); }
111
101
 
112
102
  /** Wire nodes between the input and the wet gain, for subclasses
113
103
  * @param {AudioNode} first - Node the input connects to
@@ -142,6 +132,7 @@ class AudioFilter extends AudioEffect
142
132
  super(mix);
143
133
  ASSERT(isNumber(frequency) && frequency >= 0, 'frequency must be positive or zero');
144
134
  ASSERT(isNumber(q), 'q must be a number');
135
+ if (!audioContext) return; // no audio outside a browser, where the engine runs headless
145
136
 
146
137
  /** @property {BiquadFilterNode} - The filter node */
147
138
  this.node = audioContext.createBiquadFilter();
@@ -157,6 +148,7 @@ class AudioFilter extends AudioEffect
157
148
  setFrequency(frequency, fadeTime=0)
158
149
  {
159
150
  ASSERT(isNumber(frequency) && frequency >= 0, 'frequency must be positive or zero');
151
+ if (!audioContext) return;
160
152
  this.rampParam(this.node.frequency, frequency, fadeTime);
161
153
  }
162
154
 
@@ -166,6 +158,7 @@ class AudioFilter extends AudioEffect
166
158
  setQ(q, fadeTime=0)
167
159
  {
168
160
  ASSERT(isNumber(q), 'q must be a number');
161
+ if (!audioContext) return;
169
162
  this.rampParam(this.node.Q, q, fadeTime);
170
163
  }
171
164
  }
@@ -189,6 +182,7 @@ class AudioReverb extends AudioEffect
189
182
  constructor(duration=2, decay=2, mix=.5)
190
183
  {
191
184
  super(mix);
185
+ if (!audioContext) return; // no audio outside a browser, where the engine runs headless
192
186
 
193
187
  /** @property {ConvolverNode} - The convolver node */
194
188
  this.node = audioContext.createConvolver();
@@ -203,6 +197,7 @@ class AudioReverb extends AudioEffect
203
197
  {
204
198
  ASSERT(isNumber(duration) && duration > 0, 'duration must be positive');
205
199
  ASSERT(isNumber(decay) && decay > 0, 'decay must be positive');
200
+ if (!audioContext) return;
206
201
  this.node.buffer = this.createImpulse(duration, decay);
207
202
  }
208
203
 
@@ -243,6 +238,7 @@ class AudioDelay extends AudioEffect
243
238
  constructor(time=.3, feedback=.4, mix=.5)
244
239
  {
245
240
  super(mix);
241
+ if (!audioContext) return; // no audio outside a browser, where the engine runs headless
246
242
 
247
243
  /** @property {DelayNode} - The delay node */
248
244
  this.node = audioContext.createDelay(5);
@@ -261,6 +257,7 @@ class AudioDelay extends AudioEffect
261
257
  setTime(time, fadeTime=0)
262
258
  {
263
259
  ASSERT(isNumber(time) && time >= 0 && time <= 5, 'time must be between 0 and 5');
260
+ if (!audioContext) return;
264
261
  this.rampParam(this.node.delayTime, time, fadeTime);
265
262
  }
266
263
 
@@ -270,6 +267,7 @@ class AudioDelay extends AudioEffect
270
267
  setFeedback(feedback, fadeTime=0)
271
268
  {
272
269
  ASSERT(isNumber(feedback), 'feedback must be a number');
270
+ if (!audioContext) return;
273
271
  this.rampParam(this.feedbackGain.gain, clamp(feedback, 0, .95), fadeTime);
274
272
  }
275
273
  }
@@ -292,11 +290,13 @@ class AudioDistortion extends AudioEffect
292
290
  {
293
291
  super(mix);
294
292
 
293
+ /** @property {number} - How hard the signal is driven, 0 is clean and 1 is crushed */
294
+ this.amount = clamp(amount);
295
+ if (!audioContext) return; // no audio outside a browser, where the engine runs headless
296
+
295
297
  /** @property {WaveShaperNode} - The wave shaper node */
296
298
  this.node = audioContext.createWaveShaper();
297
299
  this.node.oversample = '2x';
298
- /** @property {number} - How hard the signal is driven, 0 is clean and 1 is crushed */
299
- this.amount = amount;
300
300
  this.setAmount(amount);
301
301
  this.connectEffect(this.node);
302
302
  }
@@ -307,6 +307,7 @@ class AudioDistortion extends AudioEffect
307
307
  {
308
308
  ASSERT(isNumber(amount), 'amount must be a number');
309
309
  this.amount = amount = clamp(amount);
310
+ if (!audioContext) return;
310
311
 
311
312
  // soft clip curve, drive grows with the square of amount so low values stay subtle
312
313
  // enough points that quiet signals are still shaped at high drive, where the curve is steep near 0
@@ -343,6 +344,7 @@ class AudioCompressor extends AudioEffect
343
344
  super(mix);
344
345
  ASSERT(isNumber(threshold), 'threshold must be a number');
345
346
  ASSERT(isNumber(ratio) && ratio >= 1, 'ratio must be 1 or more');
347
+ if (!audioContext) return; // no audio outside a browser, where the engine runs headless
346
348
 
347
349
  /** @property {DynamicsCompressorNode} - The compressor node */
348
350
  this.node = audioContext.createDynamicsCompressor();
@@ -357,6 +359,7 @@ class AudioCompressor extends AudioEffect
357
359
  setThreshold(threshold, fadeTime=0)
358
360
  {
359
361
  ASSERT(isNumber(threshold), 'threshold must be a number');
362
+ if (!audioContext) return;
360
363
  this.rampParam(this.node.threshold, threshold, fadeTime);
361
364
  }
362
365
 
@@ -366,6 +369,7 @@ class AudioCompressor extends AudioEffect
366
369
  setRatio(ratio, fadeTime=0)
367
370
  {
368
371
  ASSERT(isNumber(ratio) && ratio >= 1, 'ratio must be 1 or more');
372
+ if (!audioContext) return;
369
373
  this.rampParam(this.node.ratio, ratio, fadeTime);
370
374
  }
371
375
  }