littlejsengine 1.11.13 → 1.12.4

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 (79) hide show
  1. package/dist/littlejs.d.ts +1418 -109
  2. package/dist/littlejs.esm.js +3495 -581
  3. package/dist/littlejs.esm.min.js +1 -1
  4. package/dist/littlejs.js +3258 -399
  5. package/dist/littlejs.min.js +1 -1
  6. package/dist/littlejs.release.js +3147 -379
  7. package/examples/box2d/game.js +73 -45
  8. package/examples/box2d/gameObjects.js +96 -92
  9. package/examples/box2d/index.html +2 -7
  10. package/examples/box2d/scenes.js +82 -92
  11. package/examples/breakout/game.js +62 -30
  12. package/examples/breakout/gameObjects.js +45 -47
  13. package/examples/breakout/index.html +1 -5
  14. package/examples/breakoutTutorial/game.js +36 -29
  15. package/examples/breakoutTutorial/index.html +1 -3
  16. package/examples/empty/game.js +5 -2
  17. package/examples/empty/index.html +1 -3
  18. package/examples/htmlMenu/game.js +25 -19
  19. package/examples/htmlMenu/index.html +5 -7
  20. package/examples/index.html +2 -3
  21. package/examples/module/game.js +32 -34
  22. package/examples/module/index.html +1 -1
  23. package/examples/particles/index.html +27 -22
  24. package/examples/platformer/game.js +71 -48
  25. package/examples/platformer/gameCharacter.js +42 -34
  26. package/examples/platformer/gameEffects.js +82 -75
  27. package/examples/platformer/gameLevel.js +67 -38
  28. package/examples/platformer/{gameLevelData.js → gameLevelData.json} +1 -11
  29. package/examples/platformer/gameObjects.js +70 -64
  30. package/examples/platformer/gamePlayer.js +12 -7
  31. package/examples/platformer/index.html +1 -9
  32. package/examples/puzzle/game.js +58 -42
  33. package/examples/puzzle/index.html +1 -3
  34. package/examples/shorts/base.html +2 -2
  35. package/examples/shorts/particles.js +1 -1
  36. package/examples/shorts/platformer.js +1 -1
  37. package/examples/shorts/tileLayer.js +5 -6
  38. package/examples/shorts/tiles.png +0 -0
  39. package/examples/starter/build.js +4 -4
  40. package/examples/starter/game.js +9 -12
  41. package/examples/starter/index.html +13 -13
  42. package/examples/stress/index.html +44 -43
  43. package/examples/typescript/build.js +17 -18
  44. package/examples/typescript/game.js +32 -34
  45. package/examples/typescript/game.ts +32 -34
  46. package/examples/typescript/index.html +1 -1
  47. package/examples/uiSystem/game.js +33 -36
  48. package/examples/uiSystem/index.html +1 -5
  49. package/package.json +3 -3
  50. package/plugins/box2d.js +1556 -640
  51. package/plugins/{Box2D_v2.3.1_min.wasm.js → box2d.wasm.js} +1 -1
  52. package/plugins/newgrounds.js +7 -8
  53. package/plugins/pluginExport.js +50 -0
  54. package/plugins/postProcess.js +94 -93
  55. package/plugins/uiSystem.js +145 -161
  56. package/plugins/zzfxm.js +163 -0
  57. package/reference.md +29 -27
  58. package/src/engine.js +15 -20
  59. package/src/engineAudio.js +74 -204
  60. package/src/engineBuild.js +29 -4
  61. package/src/engineDebug.js +105 -9
  62. package/src/engineDraw.js +27 -14
  63. package/src/engineExport.js +12 -8
  64. package/src/engineInput.js +3 -1
  65. package/src/engineObject.js +18 -14
  66. package/src/engineParticles.js +6 -1
  67. package/src/engineRelease.js +6 -1
  68. package/src/engineSettings.js +8 -8
  69. package/src/engineTileLayer.js +179 -96
  70. package/src/engineUtilities.js +5 -11
  71. package/src/engineWebGL.js +19 -7
  72. package/examples/starter/build/index.html +0 -2
  73. package/examples/starter/build/index.js +0 -1
  74. package/examples/starter/build/tiles.png +0 -0
  75. package/examples/starter/game.zip +0 -0
  76. package/examples/typescript/build/dist/littlejs.esm.js +0 -4834
  77. package/examples/typescript/build/examples/typescript/build.js +0 -24
  78. package/examples/typescript/build/examples/typescript/game.js +0 -102
  79. /package/plugins/{Box2D_v2.3.1_min.wasm.wasm → box2d.wasm.wasm} +0 -0
@@ -1,166 +1,150 @@
1
1
  /**
2
2
  * LittleJS User Interface Plugin
3
+ * - call new UISystemPlugin() to setup the UI system
3
4
  * - Nested Menus
4
5
  * - Text
5
6
  * - Buttons
6
7
  * - Checkboxes
7
8
  * - Images
8
- * @namespace UISystemPlugin
9
9
  */
10
10
 
11
11
  'use strict';
12
12
 
13
13
  ///////////////////////////////////////////////////////////////////////////////
14
14
 
15
- // ui defaults
16
-
17
- /** Default fill color for UI elements
18
- * @type {Color}
19
- * @memberof UISystemPlugin */
20
- let uiDefaultColor = WHITE;
21
-
22
- /** Default outline color for UI elements
23
- * @type {Color}
24
- * @memberof UISystemPlugin */
25
- let uiDefaultLineColor = BLACK;
26
-
27
- /** Default text color for UI elements
28
- * @type {Color}
29
- * @memberof UISystemPlugin */
30
- let uiDefaultTextColor = BLACK;
31
-
32
- /** Default button color for UI elements
33
- * @type {Color}
34
- * @memberof UISystemPlugin */
35
- let uiDefaultButtonColor = hsl(0,0,.5);
36
-
37
- /** Default hover color for UI elements
38
- * @type {Color}
39
- * @memberof UISystemPlugin */
40
- let uiDefaultHoverColor = hsl(0,0,.7);
41
-
42
- /** Default line width for UI elements
43
- * @type {number}
44
- * @memberof UISystemPlugin */
45
- let uiDefaultLineWidth = 4;
46
-
47
- /** Default font for UI elements
48
- * @type {string}
49
- * @memberof UISystemPlugin */
50
- let uiDefaultFont = 'arial';
51
-
52
- /** List of all UI elements
53
- * @type {Array<UIObject>}
54
- * @memberof UISystemPlugin */
55
- let uiObjects = [];
56
-
57
- /** Context to render UI elements to
58
- * @type {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D}
59
- * @memberof UISystemPlugin */
60
- let uiContext;
61
-
62
- /** Set up the UI system, typically called in gameInit
63
- * @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} [context=overlayContext]
64
- * @memberof UISystemPlugin */
65
- function initUISystem(context=overlayContext)
66
- {
67
- uiContext = context;
68
- engineAddPlugin(uiUpdate, uiRender);
15
+ /** Global UI system plugin object
16
+ * @type {UISystemPlugin} */
17
+ let uiSystem;
69
18
 
70
- // setup recursive update and render
71
- function uiUpdate()
19
+ ///////////////////////////////////////////////////////////////////////////////
20
+ /**
21
+ * UI System Global Object
22
+ */
23
+ class UISystemPlugin
24
+ {
25
+ /** Create the global UI system object
26
+ * @param {CanvasRenderingContext2D} [context]
27
+ * @example
28
+ * // create the ui plugin object
29
+ * new UISystemPlugin;
30
+ */
31
+ constructor(context=overlayContext)
72
32
  {
73
- function updateObject(o)
33
+ ASSERT(!uiSystem, 'UI system already initialized');
34
+ uiSystem = this;
35
+
36
+ /** @property {Color} - Default fill color for UI elements */
37
+ this.defaultColor = WHITE;
38
+ /** @property {Color} - Default outline color for UI elements */
39
+ this.defaultLineColor = BLACK;
40
+ /** @property {Color} - Default text color for UI elements */
41
+ this.defaultTextColor = BLACK;
42
+ /** @property {Color} - Default button color for UI elements */
43
+ this.defaultButtonColor = hsl(0,0,.5);
44
+ /** @property {Color} - Default hover color for UI elements */
45
+ this.defaultHoverColor = hsl(0,0,.7);
46
+ /** @property {number} - Default line width for UI elements */
47
+ this.defaultLineWidth = 4;
48
+ /** @property {string} - Default font for UI elements */
49
+ this.defaultFont = 'arial';
50
+ /** @property {Array<UIObject>} - List of all UI elements */
51
+ this.uiObjects = [];
52
+ /** @property {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} - Context to render UI elements to */
53
+ this.uiContext = context;
54
+
55
+ engineAddPlugin(uiUpdate, uiRender);
56
+
57
+ // setup recursive update and render
58
+ function uiUpdate()
74
59
  {
75
- if (!o.visible)
76
- return;
77
- if (o.parent)
78
- o.pos = o.localPos.add(o.parent.pos);
79
- o.update();
80
- for(const c of o.children)
81
- updateObject(c);
60
+ function updateObject(o)
61
+ {
62
+ if (!o.visible)
63
+ return;
64
+ if (o.parent)
65
+ o.pos = o.localPos.add(o.parent.pos);
66
+ o.update();
67
+ for(const c of o.children)
68
+ updateObject(c);
69
+ }
70
+ uiSystem.uiObjects.forEach(o=> o.parent || updateObject(o));
71
+ }
72
+ function uiRender()
73
+ {
74
+ function renderObject(o)
75
+ {
76
+ if (!o.visible)
77
+ return;
78
+ if (o.parent)
79
+ o.pos = o.localPos.add(o.parent.pos);
80
+ o.render();
81
+ for(const c of o.children)
82
+ renderObject(c);
83
+ }
84
+ uiSystem.uiObjects.forEach(o=> o.parent || renderObject(o));
82
85
  }
83
- uiObjects.forEach(o=> o.parent || updateObject(o));
84
86
  }
85
- function uiRender()
87
+
88
+ /** Draw a rectangle to the UI context
89
+ * @param {Vector2} pos
90
+ * @param {Vector2} size
91
+ * @param {Color} [color=uiSystem.defaultColor]
92
+ * @param {number} [lineWidth=uiSystem.defaultLineWidth]
93
+ * @param {Color} [lineColor=uiSystem.defaultLineColor] */
94
+ drawRect(pos, size, color=uiSystem.defaultColor, lineWidth=uiSystem.defaultLineWidth, lineColor=uiSystem.defaultLineColor)
86
95
  {
87
- function renderObject(o)
96
+ uiSystem.uiContext.fillStyle = color.toString();
97
+ uiSystem.uiContext.beginPath();
98
+ uiSystem.uiContext.rect(pos.x-size.x/2, pos.y-size.y/2, size.x, size.y);
99
+ uiSystem.uiContext.fill();
100
+ if (lineWidth)
88
101
  {
89
- if (!o.visible)
90
- return;
91
- if (o.parent)
92
- o.pos = o.localPos.add(o.parent.pos);
93
- o.render();
94
- for(const c of o.children)
95
- renderObject(c);
102
+ uiSystem.uiContext.strokeStyle = lineColor.toString();
103
+ uiSystem.uiContext.lineWidth = lineWidth;
104
+ uiSystem.uiContext.stroke();
96
105
  }
97
- uiObjects.forEach(o=> o.parent || renderObject(o));
98
106
  }
99
- }
100
107
 
101
- /** Draw a rectangle to the UI context
102
- * @param {Vector2} pos
103
- * @param {Vector2} size
104
- * @param {Color} [color=uiDefaultColor]
105
- * @param {number} [lineWidth=uiDefaultLineWidth]
106
- * @param {Color} [lineColor=uiDefaultLineColor]
107
- * @memberof UISystemPlugin */
108
- function drawUIRect(pos, size, color=uiDefaultColor, lineWidth=uiDefaultLineWidth, lineColor=uiDefaultLineColor)
109
- {
110
- uiContext.fillStyle = color.toString();
111
- uiContext.beginPath();
112
- uiContext.rect(pos.x-size.x/2, pos.y-size.y/2, size.x, size.y);
113
- uiContext.fill();
114
- if (lineWidth)
108
+ /** Draw a line to the UI context
109
+ * @param {Vector2} posA
110
+ * @param {Vector2} posB
111
+ * @param {number} [lineWidth=uiSystem.defaultLineWidth]
112
+ * @param {Color} [lineColor=uiSystem.defaultLineColor] */
113
+ drawLine(posA, posB, lineWidth=uiSystem.defaultLineWidth, lineColor=uiSystem.defaultLineColor)
115
114
  {
116
- uiContext.strokeStyle = lineColor.toString();
117
- uiContext.lineWidth = lineWidth;
118
- uiContext.stroke();
115
+ uiSystem.uiContext.strokeStyle = lineColor.toString();
116
+ uiSystem.uiContext.lineWidth = lineWidth;
117
+ uiSystem.uiContext.beginPath();
118
+ uiSystem.uiContext.lineTo(posA.x, posA.y);
119
+ uiSystem.uiContext.lineTo(posB.x, posB.y);
120
+ uiSystem.uiContext.stroke();
119
121
  }
120
- }
121
122
 
122
- /** Draw a line to the UI context
123
- * @param {Vector2} posA
124
- * @param {Vector2} posB
125
- * @param {number} [lineWidth=uiDefaultLineWidth]
126
- * @param {Color} [lineColor=uiDefaultLineColor]
127
- * @memberof UISystemPlugin */
128
- function drawUILine(posA, posB, lineWidth=uiDefaultLineWidth, lineColor=uiDefaultLineColor)
129
- {
130
- uiContext.strokeStyle = lineColor.toString();
131
- uiContext.lineWidth = lineWidth;
132
- uiContext.beginPath();
133
- uiContext.lineTo(posA.x, posA.y);
134
- uiContext.lineTo(posB.x, posB.y);
135
- uiContext.stroke();
136
- }
137
-
138
- /** Draw a tile to the UI context
139
- * @param {Vector2} pos
140
- * @param {Vector2} size
141
- * @param {TileInfo} tileInfo
142
- * @param {Color} [color=uiDefaultColor]
143
- * @param {number} [angle]
144
- * @param {boolean} [mirror]
145
- * @memberof UISystemPlugin */
146
- function drawUITile(pos, size, tileInfo, color=uiDefaultColor, angle=0, mirror=false)
147
- {
148
- drawTile(pos, size, tileInfo, color, angle, mirror, BLACK, false, true, uiContext);
149
- }
123
+ /** Draw a tile to the UI context
124
+ * @param {Vector2} pos
125
+ * @param {Vector2} size
126
+ * @param {TileInfo} tileInfo
127
+ * @param {Color} [color=uiSystem.defaultColor]
128
+ * @param {number} [angle]
129
+ * @param {boolean} [mirror] */
130
+ drawTile(pos, size, tileInfo, color=uiSystem.defaultColor, angle=0, mirror=false)
131
+ {
132
+ drawTile(pos, size, tileInfo, color, angle, mirror, BLACK, false, true, uiSystem.uiContext);
133
+ }
150
134
 
151
- /** Draw text to the UI context
152
- * @param {string} text
153
- * @param {Vector2} pos
154
- * @param {Vector2} size
155
- * @param {Color} [color=uiDefaultColor]
156
- * @param {number} [lineWidth=uiDefaultLineWidth]
157
- * @param {Color} [lineColor=uiDefaultLineColor]
158
- * @param {string} [align]
159
- * @param {string} [font=uiDefaultFont]
160
- * @memberof UISystemPlugin */
161
- function drawUIText(text, pos, size, color=uiDefaultColor, lineWidth=uiDefaultLineWidth, lineColor=uiDefaultLineColor, align='center', font=uiDefaultFont)
162
- {
163
- drawTextScreen(text, pos, size.y, color, lineWidth, lineColor, align, font, size.x, uiContext);
135
+ /** Draw text to the UI context
136
+ * @param {string} text
137
+ * @param {Vector2} pos
138
+ * @param {Vector2} size
139
+ * @param {Color} [color=uiSystem.defaultColor]
140
+ * @param {number} [lineWidth=uiSystem.defaultLineWidth]
141
+ * @param {Color} [lineColor=uiSystem.defaultLineColor]
142
+ * @param {string} [align]
143
+ * @param {string} [font=uiSystem.defaultFont] */
144
+ drawText(text, pos, size, color=uiSystem.defaultColor, lineWidth=uiSystem.defaultLineWidth, lineColor=uiSystem.defaultLineColor, align='center', font=uiSystem.defaultFont)
145
+ {
146
+ drawTextScreen(text, pos, size.y, color, lineWidth, lineColor, align, font, size.x, uiSystem.uiContext);
147
+ }
164
148
  }
165
149
 
166
150
  ///////////////////////////////////////////////////////////////////////////////
@@ -182,24 +166,24 @@ class UIObject
182
166
  /** @property {Vector2} - Screen space size of the object */
183
167
  this.size = size.copy();
184
168
  /** @property {Color} */
185
- this.color = uiDefaultColor;
169
+ this.color = uiSystem.defaultColor;
186
170
  /** @property {Color} */
187
- this.lineColor = uiDefaultLineColor;
171
+ this.lineColor = uiSystem.defaultLineColor;
188
172
  /** @property {Color} */
189
- this.textColor = uiDefaultTextColor;
173
+ this.textColor = uiSystem.defaultTextColor;
190
174
  /** @property {Color} */
191
- this.hoverColor = uiDefaultHoverColor;
175
+ this.hoverColor = uiSystem.defaultHoverColor;
192
176
  /** @property {number} */
193
- this.lineWidth = uiDefaultLineWidth;
177
+ this.lineWidth = uiSystem.defaultLineWidth;
194
178
  /** @property {string} */
195
- this.font = uiDefaultFont;
179
+ this.font = uiSystem.defaultFont;
196
180
  /** @property {boolean} */
197
181
  this.visible = true;
198
182
  /** @property {Array<UIObject>} */
199
183
  this.children = [];
200
184
  /** @property {UIObject} */
201
185
  this.parent = undefined;
202
- uiObjects.push(this);
186
+ uiSystem.uiObjects.push(this);
203
187
  }
204
188
 
205
189
  /** Add a child UIObject to this object
@@ -256,7 +240,7 @@ class UIObject
256
240
  render()
257
241
  {
258
242
  if (this.size.x && this.size.y)
259
- drawUIRect(this.pos, this.size, this.color, this.lineWidth, this.lineColor);
243
+ uiSystem.drawRect(this.pos, this.size, this.color, this.lineWidth, this.lineColor);
260
244
  }
261
245
 
262
246
  /** Called when the mouse enters the object */
@@ -287,9 +271,9 @@ class UIText extends UIObject
287
271
  * @param {Vector2} [size]
288
272
  * @param {string} [text]
289
273
  * @param {string} [align]
290
- * @param {string} [font=uiDefaultFont]
274
+ * @param {string} [font=uiSystem.defaultFont]
291
275
  */
292
- constructor(pos, size, text='', align='center', font=uiDefaultFont)
276
+ constructor(pos, size, text='', align='center', font=uiSystem.defaultFont)
293
277
  {
294
278
  super(pos, size);
295
279
 
@@ -303,7 +287,7 @@ class UIText extends UIObject
303
287
  }
304
288
  render()
305
289
  {
306
- drawUIText(this.text, this.pos, this.size, this.textColor, this.lineWidth, this.lineColor, this.align, this.font);
290
+ uiSystem.drawText(this.text, this.pos, this.size, this.textColor, this.lineWidth, this.lineColor, this.align, this.font);
307
291
  }
308
292
  }
309
293
 
@@ -336,7 +320,7 @@ class UITile extends UIObject
336
320
  }
337
321
  render()
338
322
  {
339
- drawUITile(this.pos, this.size, this.tileInfo, this.color, this.angle, this.mirror);
323
+ uiSystem.drawTile(this.pos, this.size, this.tileInfo, this.color, this.angle, this.mirror);
340
324
  }
341
325
  }
342
326
 
@@ -351,9 +335,9 @@ class UIButton extends UIObject
351
335
  * @param {Vector2} [pos]
352
336
  * @param {Vector2} [size]
353
337
  * @param {string} [text]
354
- * @param {Color} [color=uiDefaultButtonColor]
338
+ * @param {Color} [color=uiSystem.defaultButtonColor]
355
339
  */
356
- constructor(pos, size, text, color=uiDefaultButtonColor)
340
+ constructor(pos, size, text='', color=uiSystem.defaultButtonColor)
357
341
  {
358
342
  super(pos, size);
359
343
 
@@ -365,9 +349,9 @@ class UIButton extends UIObject
365
349
  {
366
350
  const lineColor = this.mouseIsHeld ? this.color : this.lineColor;
367
351
  const color = this.mouseIsOver? this.hoverColor : this.color;
368
- drawUIRect(this.pos, this.size, color, this.lineWidth, lineColor);
352
+ uiSystem.drawRect(this.pos, this.size, color, this.lineWidth, lineColor);
369
353
  const textSize = vec2(this.size.x, this.size.y*.8);
370
- drawUIText(this.text, this.pos, textSize,
354
+ uiSystem.drawText(this.text, this.pos, textSize,
371
355
  this.textColor, 0, undefined, this.align, this.font);
372
356
  }
373
357
  }
@@ -399,12 +383,12 @@ class UICheckbox extends UIObject
399
383
  render()
400
384
  {
401
385
  const color = this.mouseIsOver? this.hoverColor : this.color;
402
- drawUIRect(this.pos, this.size, color, this.lineWidth, this.lineColor);
386
+ uiSystem.drawRect(this.pos, this.size, color, this.lineWidth, this.lineColor);
403
387
  if (this.checked)
404
388
  {
405
389
  // draw an X if checked
406
- drawUILine(this.pos.add(this.size.multiply(vec2(-.5,-.5))), this.pos.add(this.size.multiply(vec2(.5,.5))), this.lineWidth, this.lineColor);
407
- drawUILine(this.pos.add(this.size.multiply(vec2(-.5,.5))), this.pos.add(this.size.multiply(vec2(.5,-.5))), this.lineWidth, this.lineColor);
390
+ uiSystem.drawLine(this.pos.add(this.size.multiply(vec2(-.5,-.5))), this.pos.add(this.size.multiply(vec2(.5,.5))), this.lineWidth, this.lineColor);
391
+ uiSystem.drawLine(this.pos.add(this.size.multiply(vec2(-.5,.5))), this.pos.add(this.size.multiply(vec2(.5,-.5))), this.lineWidth, this.lineColor);
408
392
  }
409
393
  }
410
394
  }
@@ -421,10 +405,10 @@ class UIScrollbar extends UIObject
421
405
  * @param {Vector2} [size]
422
406
  * @param {number} [value]
423
407
  * @param {string} [text]
424
- * @param {Color} [color=uiDefaultButtonColor]
408
+ * @param {Color} [color=uiSystem.defaultButtonColor]
425
409
  * @param {Color} [handleColor=WHITE]
426
410
  */
427
- constructor(pos, size, value=.5, text='', color=uiDefaultButtonColor, handleColor=WHITE)
411
+ constructor(pos, size, value=.5, text='', color=uiSystem.defaultButtonColor, handleColor=WHITE)
428
412
  {
429
413
  super(pos, size);
430
414
 
@@ -453,7 +437,7 @@ class UIScrollbar extends UIObject
453
437
  {
454
438
  const lineColor = this.mouseIsHeld ? this.color : this.lineColor;
455
439
  const color = this.mouseIsOver? this.hoverColor : this.color;
456
- drawUIRect(this.pos, this.size, color, this.lineWidth, lineColor);
440
+ uiSystem.drawRect(this.pos, this.size, color, this.lineWidth, lineColor);
457
441
 
458
442
  const handleSize = vec2(this.size.y);
459
443
  const handleWidth = this.size.x - handleSize.x;
@@ -461,10 +445,10 @@ class UIScrollbar extends UIObject
461
445
  const p2 = this.pos.x + handleWidth/2;
462
446
  const handlePos = vec2(lerp(this.value, p1, p2), this.pos.y);
463
447
  const barColor = this.mouseIsHeld ? this.color : this.handleColor;
464
- drawUIRect(handlePos, handleSize, barColor, this.lineWidth, this.lineColor);
448
+ uiSystem.drawRect(handlePos, handleSize, barColor, this.lineWidth, this.lineColor);
465
449
 
466
450
  const textSize = vec2(this.size.x, this.size.y*.8);
467
- drawUIText(this.text, this.pos, textSize,
451
+ uiSystem.drawText(this.text, this.pos, textSize,
468
452
  this.textColor, 0, undefined, this.align, this.font);
469
453
  }
470
- }
454
+ }
@@ -0,0 +1,163 @@
1
+ /**
2
+ * LittleJS ZzFXM Plugin
3
+ */
4
+
5
+ 'use strict';
6
+
7
+ /**
8
+ * Music Object - Stores a zzfx music track for later use
9
+ *
10
+ * <a href=https://keithclark.github.io/ZzFXM/>Create music with the ZzFXM tracker.</a>
11
+ * @example
12
+ * // create some music
13
+ * const music_example = new Music(
14
+ * [
15
+ * [ // instruments
16
+ * [,0,400] // simple note
17
+ * ],
18
+ * [ // patterns
19
+ * [ // pattern 1
20
+ * [ // channel 0
21
+ * 0, -1, // instrument 0, left speaker
22
+ * 1, 0, 9, 1 // channel notes
23
+ * ],
24
+ * [ // channel 1
25
+ * 0, 1, // instrument 0, right speaker
26
+ * 0, 12, 17, -1 // channel notes
27
+ * ]
28
+ * ],
29
+ * ],
30
+ * [0, 0, 0, 0], // sequence, play pattern 0 four times
31
+ * 90 // BPM
32
+ * ]);
33
+ *
34
+ * // play the music
35
+ * music_example.play();
36
+ */
37
+ class ZzFXMusic extends Sound
38
+ {
39
+ /** Create a music object and cache the zzfx music samples for later use
40
+ * @param {[Array, Array, Array, number]} zzfxMusic - Array of zzfx music parameters
41
+ */
42
+ constructor(zzfxMusic)
43
+ {
44
+ super(undefined);
45
+
46
+ if (!soundEnable || headlessMode) return;
47
+ this.randomness = 0;
48
+ this.sampleChannels = zzfxM(...zzfxMusic);
49
+ this.sampleRate = zzfxR;
50
+ }
51
+
52
+ /** Play the music
53
+ * @param {number} [volume=1] - How much to scale volume by
54
+ * @param {boolean} [loop] - True if the music should loop
55
+ * @return {AudioBufferSourceNode} - The audio source node
56
+ */
57
+ playMusic(volume, loop=false)
58
+ { return super.play(undefined, volume, 1, 1, loop); }
59
+ }
60
+
61
+ ///////////////////////////////////////////////////////////////////////////////
62
+ // ZzFX Music Renderer v2.0.3 by Keith Clark and Frank Force
63
+
64
+ /** Generate samples for a ZzFM song with given parameters
65
+ * @param {Array} instruments - Array of ZzFX sound parameters
66
+ * @param {Array} patterns - Array of pattern data
67
+ * @param {Array} sequence - Array of pattern indexes
68
+ * @param {number} [BPM] - Playback speed of the song in BPM
69
+ * @return {Array} - Left and right channel sample data */
70
+ function zzfxM(instruments, patterns, sequence, BPM = 125)
71
+ {
72
+ let i, j, k;
73
+ let instrumentParameters;
74
+ let note;
75
+ let sample;
76
+ let patternChannel;
77
+ let notFirstBeat;
78
+ let stop;
79
+ let instrument;
80
+ let attenuation;
81
+ let outSampleOffset;
82
+ let isSequenceEnd;
83
+ let sampleOffset = 0;
84
+ let nextSampleOffset;
85
+ let sampleBuffer = [];
86
+ let leftChannelBuffer = [];
87
+ let rightChannelBuffer = [];
88
+ let channelIndex = 0;
89
+ let panning = 0;
90
+ let hasMore = 1;
91
+ let sampleCache = {};
92
+ let beatLength = zzfxR / BPM * 60 >> 2;
93
+
94
+ // for each channel in order until there are no more
95
+ for (; hasMore; channelIndex++) {
96
+
97
+ // reset current values
98
+ sampleBuffer = [hasMore = notFirstBeat = outSampleOffset = 0];
99
+
100
+ // for each pattern in sequence
101
+ sequence.forEach((patternIndex, sequenceIndex) => {
102
+ // get pattern for current channel, use empty 1 note pattern if none found
103
+ patternChannel = patterns[patternIndex][channelIndex] || [0, 0, 0];
104
+
105
+ // check if there are more channels
106
+ hasMore |= patterns[patternIndex][channelIndex]&&1;
107
+
108
+ // get next offset, use the length of first channel
109
+ nextSampleOffset = outSampleOffset + (patterns[patternIndex][0].length - 2 - (notFirstBeat?0:1)) * beatLength;
110
+ // for each beat in pattern, plus one extra if end of sequence
111
+ isSequenceEnd = sequenceIndex == sequence.length - 1;
112
+ for (i = 2, k = outSampleOffset; i < patternChannel.length + isSequenceEnd; notFirstBeat = ++i) {
113
+
114
+ // <channel-note>
115
+ note = patternChannel[i];
116
+
117
+ // stop if end, different instrument or new note
118
+ stop = i == patternChannel.length + isSequenceEnd - 1 && isSequenceEnd ||
119
+ instrument != (patternChannel[0] || 0) || note | 0;
120
+
121
+ // fill buffer with samples for previous beat, most cpu intensive part
122
+ for (j = 0; j < beatLength && notFirstBeat;
123
+
124
+ // fade off attenuation at end of beat if stopping note, prevents clicking
125
+ j++ > beatLength - 99 && stop && attenuation < 1? attenuation += 1 / 99 : 0
126
+ ) {
127
+ // copy sample to stereo buffers with panning
128
+ sample = (1 - attenuation) * sampleBuffer[sampleOffset++] / 2 || 0;
129
+ leftChannelBuffer[k] = (leftChannelBuffer[k] || 0) - sample * panning + sample;
130
+ rightChannelBuffer[k] = (rightChannelBuffer[k++] || 0) + sample * panning + sample;
131
+ }
132
+
133
+ // set up for next note
134
+ if (note) {
135
+ // set attenuation
136
+ attenuation = note % 1;
137
+ panning = patternChannel[1] || 0;
138
+ if (note |= 0) {
139
+ // get cached sample
140
+ sampleBuffer = sampleCache[
141
+ [
142
+ instrument = patternChannel[sampleOffset = 0] || 0,
143
+ note
144
+ ]
145
+ ] = sampleCache[[instrument, note]] || (
146
+ // add sample to cache
147
+ instrumentParameters = [...instruments[instrument]],
148
+ instrumentParameters[2] = (instrumentParameters[2] || 220) * 2**(note / 12 - 1),
149
+
150
+ // allow negative values to stop notes
151
+ note > 0 ? zzfxG(...instrumentParameters) : []
152
+ );
153
+ }
154
+ }
155
+ }
156
+
157
+ // update the sample offset
158
+ outSampleOffset = nextSampleOffset;
159
+ });
160
+ }
161
+
162
+ return [leftChannelBuffer, rightChannelBuffer];
163
+ }