@litecanvas/utils 0.24.1 → 0.24.2

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/dist/actor.js CHANGED
@@ -33,8 +33,8 @@
33
33
  /** @type {number} */
34
34
  y;
35
35
  /**
36
- * @param {number} x
37
- * @param {number} y
36
+ * @param {number} [x=0]
37
+ * @param {number} [y]
38
38
  */
39
39
  constructor(x = 0, y = x) {
40
40
  this.x = x;
@@ -56,6 +56,972 @@
56
56
  return new Vector(x, y);
57
57
  };
58
58
 
59
+ // node_modules/litecanvas/src/zzfx.js
60
+ var zzfxX = /* @__PURE__ */ new AudioContext();
61
+ var zzfx = (p = 1, k = 0.05, b = 220, e = 0, r = 0, t = 0.1, q = 0, D = 1, u = 0, y = 0, v = 0, z = 0, l = 0, E = 0, A = 0, F = 0, c = 0, w = 1, m = 0, B = 0, N = 0) => {
62
+ let M = Math, d = 2 * M.PI, R = 44100, G = u *= 500 * d / R / R, C = b *= (1 - k + 2 * k * M.random(k = [])) * d / R, g = 0, H = 0, a = 0, n = 1, I = 0, J = 0, f = 0, h = N < 0 ? -1 : 1, x = d * h * N * 2 / R, L = M.cos(x), Z = M.sin, K = Z(x) / 4, O = 1 + K, X = -2 * L / O, Y = (1 - K) / O, P = (1 + h * L) / 2 / O, Q = -(h + L) / O, S = P, T = 0, U = 0, V = 0, W = 0;
63
+ e = R * e + 9;
64
+ m *= R;
65
+ r *= R;
66
+ t *= R;
67
+ c *= R;
68
+ y *= 500 * d / R ** 3;
69
+ A *= d / R;
70
+ v *= d / R;
71
+ z *= R;
72
+ l = R * l | 0;
73
+ p *= 0.3 * (globalThis.zzfxV || 1);
74
+ for (h = e + m + r + t + c | 0; a < h; k[a++] = f * p)
75
+ ++J % (100 * F | 0) || (f = q ? 1 < q ? 2 < q ? 3 < q ? Z(g * g) : M.max(M.min(M.tan(g), 1), -1) : 1 - (2 * g / d % 2 + 2) % 2 : 1 - 4 * M.abs(M.round(g / d) - g / d) : Z(g), f = (l ? 1 - B + B * Z(d * a / l) : 1) * (f < 0 ? -1 : 1) * M.abs(f) ** D * (a < e ? a / e : a < e + m ? 1 - (a - e) / m * (1 - w) : a < e + m + r ? w : a < h - c ? (h - a - c) / t * w : 0), f = c ? f / 2 + (c > a ? 0 : (a < h - c ? 1 : (h - a) / c) * k[a - c | 0] / 2 / p) : f, N ? f = W = S * T + Q * (T = U) + P * (U = f) - Y * V - X * (V = W) : 0), x = (b += u += y) * M.cos(A * H++), g += x + x * E * Z(a ** 5), n && ++n > z && (b += v, C += v, n = 0), !l || ++I % l || (b = C, u = G, n = n || 1);
76
+ p = zzfxX.createBuffer(1, h, R);
77
+ p.getChannelData(0).set(k);
78
+ b = zzfxX.createBufferSource();
79
+ b.buffer = p;
80
+ b.connect(zzfxX.destination);
81
+ b.start();
82
+ };
83
+
84
+ // node_modules/litecanvas/src/palette.js
85
+ var colors = [
86
+ "#111",
87
+ "#6a7799",
88
+ "#aec2c2",
89
+ "#FFF1E8",
90
+ "#e83b3b",
91
+ "#fabc20",
92
+ "#155fd9",
93
+ "#3cbcfc",
94
+ "#327345",
95
+ "#63c64d",
96
+ "#6c2c1f",
97
+ "#ac7c00"
98
+ ];
99
+
100
+ // node_modules/litecanvas/src/index.js
101
+ globalThis.litecanvas = litecanvas;
102
+ function litecanvas(settings = {}) {
103
+ const root = globalThis, PI = Math.PI, TWO_PI = PI * 2, raf = requestAnimationFrame, _browserEventListeners = [], on = (elem, evt, callback) => {
104
+ elem.addEventListener(evt, callback, false);
105
+ _browserEventListeners.push(
106
+ () => elem.removeEventListener(evt, callback, false)
107
+ );
108
+ }, defaults = {
109
+ fps: 60,
110
+ fullscreen: true,
111
+ width: null,
112
+ height: null,
113
+ autoscale: true,
114
+ pixelart: false,
115
+ antialias: false,
116
+ canvas: null,
117
+ global: true,
118
+ loop: null,
119
+ pauseOnBlur: true,
120
+ tapEvents: true,
121
+ keyboardEvents: true,
122
+ animate: true
123
+ };
124
+ settings = Object.assign(defaults, settings);
125
+ let _initialized = false, _plugins = [], _canvas = settings.canvas || document.createElement("canvas"), _fullscreen = settings.fullscreen, _autoscale = settings.autoscale, _animated = settings.animate, _scale = 1, _ctx, _timeScale = 1, _lastFrame, _step, _stepMs, _accumulated = 0, _focused = true, _fontFamily = "sans-serif", _fontStyle = "", _fontSize = 32, _rng_seed = Date.now(), _global = settings.global, _events = {
126
+ init: null,
127
+ update: null,
128
+ draw: null,
129
+ resized: null,
130
+ tap: null,
131
+ untap: null,
132
+ tapping: null,
133
+ tapped: null
134
+ }, _helpers = {
135
+ settings: Object.assign({}, settings),
136
+ colors
137
+ };
138
+ const instance = {
139
+ /** @type {number} */
140
+ WIDTH: settings.width,
141
+ /** @type {number} */
142
+ HEIGHT: settings.height || settings.width,
143
+ /** @type {HTMLCanvasElement} */
144
+ CANVAS: null,
145
+ /** @type {number} */
146
+ ELAPSED: 0,
147
+ /** @type {number} */
148
+ CENTERX: 0,
149
+ /** @type {number} */
150
+ CENTERY: 0,
151
+ /** @type {number} */
152
+ MOUSEX: -1,
153
+ /** @type {number} */
154
+ MOUSEY: -1,
155
+ /** @type {number[]} */
156
+ DEFAULT_SFX: [0.5, , 1675, , 0.06, 0.2, 1, 1.8, , , 637, 0.06],
157
+ /** MATH API */
158
+ /**
159
+ * The value of the mathematical constant PI (π).
160
+ * Approximately 3.14159
161
+ *
162
+ * @type {number}
163
+ */
164
+ PI,
165
+ /**
166
+ * Twice the value of the mathematical constant PI (π).
167
+ * Approximately 6.28318
168
+ *
169
+ * Note: TWO_PI radians equals 360°, PI radians equals 180°,
170
+ * HALF_PI radians equals 90°, and HALF_PI/2 radians equals 45°.
171
+ *
172
+ * @type {number}
173
+ */
174
+ TWO_PI,
175
+ /**
176
+ * Half the value of the mathematical constant PI (π).
177
+ * Approximately 1.57079
178
+ *
179
+ * @type {number}
180
+ */
181
+ HALF_PI: PI * 0.5,
182
+ /**
183
+ * Calculates a linear (interpolation) value over t%.
184
+ *
185
+ * @param {number} start
186
+ * @param {number} end
187
+ * @param {number} t The progress in percentage, where 0 = 0% and 1 = 100%.
188
+ * @returns {number} The unterpolated value
189
+ * @tutorial https://gamedev.net/tutorials/programming/general-and-gameplay-programming/a-brief-introduction-to-lerp-r4954/
190
+ */
191
+ lerp: (start, end, t) => start + t * (end - start),
192
+ /**
193
+ * Convert degrees to radians
194
+ *
195
+ * @param {number} degs
196
+ * @returns {number} the value in radians
197
+ */
198
+ deg2rad: (degs) => PI / 180 * degs,
199
+ /**
200
+ * Convert radians to degrees
201
+ *
202
+ * @param {number} rads
203
+ * @returns {number} the value in degrees
204
+ */
205
+ rad2deg: (rads) => 180 / PI * rads,
206
+ /**
207
+ * Constrains a number between `min` and `max`.
208
+ *
209
+ * @param {number} value
210
+ * @param {number} min
211
+ * @param {number} max
212
+ * @returns {number}
213
+ */
214
+ clamp: (value, min, max) => {
215
+ if (value < min) return min;
216
+ if (value > max) return max;
217
+ return value;
218
+ },
219
+ /**
220
+ * Wraps a number between `min` (inclusive) and `max` (exclusive).
221
+ *
222
+ * @param {number} value
223
+ * @param {number} min
224
+ * @param {number} max
225
+ * @returns {number}
226
+ */
227
+ wrap: (value, min, max) => value - (max - min) * Math.floor((value - min) / (max - min)),
228
+ /**
229
+ * Re-maps a number from one range to another.
230
+ *
231
+ * @param {number} value the value to be remapped.
232
+ * @param {number} min1 lower bound of the value's current range.
233
+ * @param {number} max1 upper bound of the value's current range.
234
+ * @param {number} min2 lower bound of the value's target range.
235
+ * @param {number} max2 upper bound of the value's target range.
236
+ * @param {boolean} [withinBounds=false] constrain the value to the newly mapped range
237
+ * @returns {number} the remapped number
238
+ */
239
+ map(value, min1, max1, min2, max2, withinBounds) {
240
+ const result = (value - min1) / (max1 - min1) * (max2 - min2) + min2;
241
+ return withinBounds ? instance.clamp(result, min2, max2) : result;
242
+ },
243
+ /**
244
+ * Maps a number from one range to a value between 0 and 1.
245
+ * Identical to `map(value, min, max, 0, 1)`.
246
+ * Note: Numbers outside the range are not clamped to 0 and 1.
247
+ *
248
+ * @param {number} value
249
+ * @param {number} min
250
+ * @param {number} min
251
+ * @returns {number} the normalized number.
252
+ */
253
+ norm: (value, min, max) => instance.map(value, min, max, 0, 1),
254
+ /** RNG API */
255
+ /**
256
+ * Generates a pseudorandom float between min (inclusive) and max (exclusive)
257
+ * using the Linear Congruential Generator (LCG) algorithm.
258
+ *
259
+ * @param {number} [min=0.0]
260
+ * @param {number} [max=1.0]
261
+ * @returns {number} the random number
262
+ */
263
+ rand: (min = 0, max = 1) => {
264
+ const a = 1664525;
265
+ const c = 1013904223;
266
+ const m = 4294967296;
267
+ _rng_seed = (a * _rng_seed + c) % m;
268
+ return _rng_seed / m * (max - min) + min;
269
+ },
270
+ /**
271
+ * Generates a pseudorandom integer between min (inclusive) and max (inclusive)
272
+ *
273
+ * @param {number} [min=0]
274
+ * @param {number} [max=1]
275
+ * @returns {number} the random number
276
+ */
277
+ randi: (min = 0, max = 1) => Math.floor(instance.rand(min, max + 1)),
278
+ /**
279
+ * If a value is passed, initializes the random number generator with an explicit seed value.
280
+ * Otherwise, returns the current seed state.
281
+ *
282
+ * @param {number} value
283
+ * @returns {number} the seed state
284
+ */
285
+ seed: (value) => {
286
+ return null == value ? _rng_seed : _rng_seed = ~~value;
287
+ },
288
+ /** BASIC GRAPHICS API */
289
+ /**
290
+ * Clear the game screen
291
+ *
292
+ * @param {number|null} color The background color (from 0 to 7) or null (for transparent)
293
+ */
294
+ cls(color) {
295
+ if (null == color) {
296
+ _ctx.clearRect(0, 0, instance.WIDTH, instance.HEIGHT);
297
+ } else {
298
+ instance.rectfill(0, 0, instance.WIDTH, instance.HEIGHT, color);
299
+ }
300
+ },
301
+ /**
302
+ * Draw a rectangle outline
303
+ *
304
+ * @param {number} x
305
+ * @param {number} y
306
+ * @param {number} width
307
+ * @param {number} height
308
+ * @param {number} [color=0] the color index (generally from 0 to 7)
309
+ * @param {number|number[]} [radii] A number or list specifying the radii used to draw a rounded-borders rectangle
310
+ */
311
+ rect(x, y, width, height, color = 0, radii = null) {
312
+ _ctx.beginPath();
313
+ _ctx[radii ? "roundRect" : "rect"](~~x, ~~y, width, height, radii);
314
+ instance.stroke(color);
315
+ },
316
+ /**
317
+ * Draw a color-filled rectangle
318
+ *
319
+ * @param {number} x
320
+ * @param {number} y
321
+ * @param {number} width
322
+ * @param {number} height
323
+ * @param {number} [color=0] the color index (generally from 0 to 7)
324
+ * @param {number|number[]} [radii] A number or list specifying the radii used to draw a rounded-borders rectangle
325
+ */
326
+ rectfill(x, y, width, height, color = 0, radii = null) {
327
+ _ctx.beginPath();
328
+ _ctx[radii ? "roundRect" : "rect"](~~x, ~~y, width, height, radii);
329
+ instance.fill(color);
330
+ },
331
+ /**
332
+ * Draw a circle outline
333
+ *
334
+ * @param {number} x
335
+ * @param {number} y
336
+ * @param {number} radius
337
+ * @param {number} [color=0] the color index (generally from 0 to 7)
338
+ */
339
+ circ(x, y, radius, color) {
340
+ _ctx.beginPath();
341
+ _ctx.arc(~~x, ~~y, radius, 0, TWO_PI);
342
+ instance.stroke(color);
343
+ },
344
+ /**
345
+ * Draw a color-filled circle
346
+ *
347
+ * @param {number} x
348
+ * @param {number} y
349
+ * @param {number} radius
350
+ * @param {number} [color=0] the color index (generally from 0 to 7)
351
+ */
352
+ circfill(x, y, radius, color) {
353
+ _ctx.beginPath();
354
+ _ctx.arc(~~x, ~~y, radius, 0, TWO_PI);
355
+ instance.fill(color);
356
+ },
357
+ /**
358
+ * Draw a line
359
+ *
360
+ * @param {number} x1
361
+ * @param {number} y1
362
+ * @param {number} x2
363
+ * @param {number} y2
364
+ * @param {number} [color=0] the color index (generally from 0 to 7)
365
+ */
366
+ line(x1, y1, x2, y2, color) {
367
+ _ctx.beginPath();
368
+ _ctx.moveTo(~~x1, ~~y1);
369
+ _ctx.lineTo(~~x2, ~~y2);
370
+ instance.stroke(color);
371
+ },
372
+ /**
373
+ * Sets the thickness of lines
374
+ *
375
+ * @param {number} value
376
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/lineWidth
377
+ */
378
+ linewidth(value) {
379
+ _ctx.lineWidth = value;
380
+ },
381
+ /**
382
+ * Sets the line dash pattern used when drawing lines
383
+ *
384
+ * @param {number[]} segments the line dash pattern
385
+ * @param {number} [offset=0] the line dash offset, or "phase".
386
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/setLineDash
387
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/lineDashOffset
388
+ */
389
+ linedash(segments, offset = 0) {
390
+ _ctx.setLineDash(segments);
391
+ _ctx.lineDashOffset = offset;
392
+ },
393
+ /** TEXT RENDERING API */
394
+ /**
395
+ * Draw text
396
+ *
397
+ * @param {number} x
398
+ * @param {number} y
399
+ * @param {string} text the text message
400
+ * @param {number} [color=3] the color index (generally from 0 to 7)
401
+ */
402
+ text(x, y, text, color = 3) {
403
+ _ctx.font = `${_fontStyle} ${_fontSize}px ${_fontFamily}`;
404
+ _ctx.fillStyle = instance.getcolor(color);
405
+ _ctx.fillText(text, ~~x, ~~y);
406
+ },
407
+ /**
408
+ * Set the font family
409
+ *
410
+ * @param {string} fontFamily
411
+ */
412
+ textfont(fontFamily) {
413
+ _fontFamily = fontFamily;
414
+ },
415
+ /**
416
+ * Set the font size
417
+ *
418
+ * @param {string} size
419
+ */
420
+ textsize(size) {
421
+ _fontSize = size;
422
+ },
423
+ /**
424
+ * Sets whether a font should be styled with a "normal", "italic", or "bold".
425
+ *
426
+ * @param {string} style
427
+ */
428
+ textstyle(style) {
429
+ _fontStyle = style || "";
430
+ },
431
+ /**
432
+ * Sets the alignment used when drawing texts
433
+ *
434
+ * @param {string} align the horizontal alignment. Possible values: "left", "right", "center", "start" or "end"
435
+ * @param {string} baseline the vertical alignment. Possible values: "top", "bottom", "middle", "hanging" or "ideographic"
436
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/textBaseline
437
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/textAlign
438
+ */
439
+ textalign(align, baseline) {
440
+ if (align) _ctx.textAlign = align;
441
+ if (baseline) _ctx.textBaseline = baseline;
442
+ },
443
+ /**
444
+ * Returns a TextMetrics object that contains information about the measured text (such as its width, for example)
445
+ *
446
+ * @param {string} text
447
+ * @param {number} [size]
448
+ * @returns {TextMetrics}
449
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/TextMetrics
450
+ */
451
+ textmetrics(text, size = _fontSize) {
452
+ _ctx.font = `${_fontStyle} ${size}px ${_fontFamily}`;
453
+ const metrics = _ctx.measureText(text);
454
+ metrics.height = metrics.actualBoundingBoxAscent + metrics.actualBoundingBoxDescent;
455
+ return metrics;
456
+ },
457
+ /** IMAGE GRAPHICS API */
458
+ /**
459
+ * Draw an image
460
+ *
461
+ * @param {number} x
462
+ * @param {number} y
463
+ * @param {OffscreenCanvas|HTMLImageElement|HTMLCanvasElement} image
464
+ */
465
+ image(x, y, image) {
466
+ _ctx.drawImage(image, ~~x, ~~y);
467
+ },
468
+ /**
469
+ * Creates a offscreen canvas to draw on it
470
+ *
471
+ * @param {number} width
472
+ * @param {number} height
473
+ * @param {string[]|drawCallback} draw
474
+ * @param {object} [options]
475
+ * @param {number} [options.scale=1]
476
+ * @param {OffscreenCanvas | HTMLCanvasElement} [options.canvas]
477
+ * @returns {OffscreenCanvas}
478
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/OffscreenCanvas
479
+ */
480
+ paint(width, height, draw, options = {}) {
481
+ const canvas = options.canvas || new OffscreenCanvas(1, 1), scale = options.scale || 1, contextOriginal = _ctx;
482
+ canvas.width = width * scale;
483
+ canvas.height = height * scale;
484
+ _ctx = canvas.getContext("2d");
485
+ _ctx.scale(scale, scale);
486
+ if (draw.push) {
487
+ let x = 0, y = 0;
488
+ _ctx.imageSmoothingEnabled = false;
489
+ for (const str of draw) {
490
+ for (const color of str) {
491
+ if (" " !== color && "." !== color) {
492
+ instance.rectfill(x, y, 1, 1, parseInt(color, 16));
493
+ }
494
+ x++;
495
+ }
496
+ y++;
497
+ x = 0;
498
+ }
499
+ } else {
500
+ draw(_ctx);
501
+ }
502
+ _ctx = contextOriginal;
503
+ return canvas;
504
+ },
505
+ /** ADVANCED GRAPHICS API */
506
+ /**
507
+ * Get or set the canvas context 2D
508
+ *
509
+ * @param {CanvasRenderingContext2D} [context]
510
+ * @returns {CanvasRenderingContext2D}
511
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D
512
+ */
513
+ ctx(context) {
514
+ if (context) {
515
+ _ctx = context;
516
+ }
517
+ return _ctx;
518
+ },
519
+ /**
520
+ * saves the current drawing style settings and transformations
521
+ *
522
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/save
523
+ */
524
+ push: () => _ctx.save(),
525
+ /**
526
+ * restores the drawing style settings and transformations
527
+ *
528
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/restore
529
+ */
530
+ pop: () => _ctx.restore(),
531
+ /**
532
+ * Adds a translation to the transformation matrix.
533
+ *
534
+ * @param {number} x
535
+ * @param {number} y
536
+ */
537
+ translate: (x, y) => _ctx.translate(~~x, ~~y),
538
+ /**
539
+ * Adds a scaling transformation to the canvas units horizontally and/or vertically.
540
+ *
541
+ * @param {number} x
542
+ * @param {number} [y]
543
+ */
544
+ scale: (x, y) => _ctx.scale(x, y || x),
545
+ /**
546
+ * Adds a rotation to the transformation matrix.
547
+ *
548
+ * @param {number} radians
549
+ */
550
+ rotate: (radians) => _ctx.rotate(radians),
551
+ /**
552
+ * @param {number} a
553
+ * @param {number} b
554
+ * @param {number} c
555
+ * @param {number} d
556
+ * @param {number} e
557
+ * @param {number} f
558
+ * @param {boolean} [resetFirst=true] `false` to use _ctx.transform(); by default use _ctx.setTransform()
559
+ *
560
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/setTransform
561
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/transform
562
+ */
563
+ transform: (a, b, c, d, e, f, resetFirst = true) => _ctx[resetFirst ? "setTransform" : "transform"](a, b, c, d, e, f),
564
+ /**
565
+ * Sets the alpha (opacity) value to apply when drawing new shapes and images
566
+ *
567
+ * @param {number} alpha float from 0 to 1 (e.g: 0.5 = 50% transparent)
568
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/globalAlpha
569
+ */
570
+ alpha(value) {
571
+ _ctx.globalAlpha = instance.clamp(value, 0, 1);
572
+ },
573
+ /**
574
+ * Returns a newly instantiated Path2D object, optionally with another
575
+ * path as an argument (creates a copy), or optionally with a string
576
+ * consisting of SVG path data.
577
+ *
578
+ * @param {Path2D|string} [arg]
579
+ * @returns Path2D
580
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/Path2D/Path2D
581
+ */
582
+ path: (arg) => new Path2D(arg),
583
+ /**
584
+ * Fills the current or given path with a given color.
585
+ *
586
+ * @param {number} [color=0]
587
+ * @param {Path2D} [path]
588
+ */
589
+ fill(color, path) {
590
+ _ctx.fillStyle = instance.getcolor(color);
591
+ if (path) {
592
+ _ctx.fill(path);
593
+ } else {
594
+ _ctx.fill();
595
+ }
596
+ },
597
+ /**
598
+ * Outlines the current or given path with a given color.
599
+ *
600
+ * @param {number} [color=0]
601
+ * @param {Path2D} [path]
602
+ */
603
+ stroke(color, path) {
604
+ _ctx.strokeStyle = instance.getcolor(color);
605
+ if (path) {
606
+ _ctx.stroke(path);
607
+ } else {
608
+ _ctx.stroke();
609
+ }
610
+ },
611
+ /**
612
+ * Turn given path into a clipping region.
613
+ *
614
+ * @param {Path2D} path
615
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/clip
616
+ */
617
+ clip(path) {
618
+ _ctx.clip(path);
619
+ },
620
+ /** SOUND API */
621
+ /**
622
+ * Play a sound effects using ZzFX library.
623
+ * If the first argument is omitted, plays an default sound.
624
+ *
625
+ * @param {number[]} [zzfxParams] a ZzFX array of params
626
+ * @param {number} [pitchSlide] a value to increment/decrement the pitch
627
+ * @param {number} [volumeFactor] the volume factor
628
+ * @returns {number[] | boolean} The sound that was played or `false`
629
+ *
630
+ * @see https://github.com/KilledByAPixel/ZzFX
631
+ */
632
+ sfx(zzfxParams, pitchSlide = 0, volumeFactor = 1) {
633
+ if (root.zzfxV <= 0 || navigator.userActivation && !navigator.userActivation.hasBeenActive) {
634
+ return false;
635
+ }
636
+ zzfxParams = zzfxParams || instance.DEFAULT_SFX;
637
+ if (pitchSlide > 0 || volumeFactor !== 1) {
638
+ zzfxParams = zzfxParams.slice();
639
+ zzfxParams[0] = volumeFactor * (zzfxParams[0] || 1);
640
+ zzfxParams[10] = ~~zzfxParams[10] + pitchSlide;
641
+ }
642
+ zzfx.apply(0, zzfxParams);
643
+ return zzfxParams;
644
+ },
645
+ /**
646
+ * Set the ZzFX's global volume factor.
647
+ * Note: use 0 to mute all sound effects.
648
+ *
649
+ * @param {number} value
650
+ */
651
+ volume(value) {
652
+ root.zzfxV = +value;
653
+ },
654
+ /** UTILS API */
655
+ /**
656
+ * Check a collision between two rectangles
657
+ *
658
+ * @param {number} x1 first rectangle position X
659
+ * @param {number} y1 first rectangle position Y
660
+ * @param {number} w1 first rectangle width
661
+ * @param {number} h1 first rectangle height
662
+ * @param {number} x2 second rectangle position X
663
+ * @param {number} y2 second rectangle position Y
664
+ * @param {number} w2 second rectangle width
665
+ * @param {number} h2 second rectangle height
666
+ * @returns {boolean}
667
+ */
668
+ colrect: (x1, y1, w1, h1, x2, y2, w2, h2) => x1 < x2 + w2 && x1 + w1 > x2 && y1 < y2 + h2 && y1 + h1 > y2,
669
+ /**
670
+ * Check a collision between two circles
671
+ *
672
+ * @param {number} x1 first circle position X
673
+ * @param {number} y1 first circle position Y
674
+ * @param {number} r1 first circle position radius
675
+ * @param {number} x2 second circle position X
676
+ * @param {number} y2 second circle position Y
677
+ * @param {number} r2 second circle position radius
678
+ * @returns {boolean}
679
+ */
680
+ colcirc: (x1, y1, r1, x2, y2, r2) => (x2 - x1) ** 2 + (y2 - y1) ** 2 <= (r1 + r2) ** 2,
681
+ /** PLUGINS API */
682
+ /**
683
+ * Prepares a plugin to be loaded
684
+ *
685
+ * @param {pluginCallback} callback
686
+ */
687
+ use(callback, config = {}) {
688
+ _initialized ? loadPlugin(callback, config) : _plugins.push([callback, config]);
689
+ },
690
+ /**
691
+ * Add a game event listener
692
+ *
693
+ * @param {string} eventName the event type name
694
+ * @param {Function} callback the function that is called when the event occurs
695
+ * @returns {Function} a function to remove the listener
696
+ */
697
+ listen(eventName, callback) {
698
+ _events[eventName] = _events[eventName] || /* @__PURE__ */ new Set();
699
+ _events[eventName].add(callback);
700
+ return () => _events[eventName].delete(callback);
701
+ },
702
+ /**
703
+ * Call all listeners attached to a game event
704
+ *
705
+ * @param {string} eventName The event type name
706
+ * @param {*} [arg1] any data to be passed over the listeners
707
+ * @param {*} [arg2] any data to be passed over the listeners
708
+ * @param {*} [arg3] any data to be passed over the listeners
709
+ * @param {*} [arg4] any data to be passed over the listeners
710
+ */
711
+ emit(eventName, arg1, arg2, arg3, arg4) {
712
+ triggerEvent("before:" + eventName, arg1, arg2, arg3, arg4);
713
+ triggerEvent(eventName, arg1, arg2, arg3, arg4);
714
+ triggerEvent("after:" + eventName, arg1, arg2, arg3, arg4);
715
+ },
716
+ /**
717
+ * Get a color by index
718
+ *
719
+ * @param {number} [index=0] The color number
720
+ * @returns {string} the color code
721
+ */
722
+ getcolor: (index) => colors[~~index % colors.length],
723
+ /**
724
+ * Create or update a instance variable
725
+ *
726
+ * @param {string} key
727
+ * @param {*} value
728
+ */
729
+ setvar(key, value) {
730
+ instance[key] = value;
731
+ if (_global) {
732
+ root[key] = value;
733
+ }
734
+ },
735
+ /**
736
+ * Resizes the game canvas and emit the "resized" event
737
+ *
738
+ * @param {number} width
739
+ * @param {number} height
740
+ */
741
+ resize(width, height) {
742
+ instance.setvar("WIDTH", _canvas.width = width);
743
+ instance.setvar("HEIGHT", _canvas.height = height || width);
744
+ pageResized();
745
+ },
746
+ /**
747
+ * The scale of the game's delta time (dt).
748
+ * Values higher than 1 increase the speed of time, while values smaller than 1 decrease it.
749
+ * A value of 0 freezes time and is effectively equivalent to pausing.
750
+ *
751
+ * @param {number} value
752
+ */
753
+ timescale(value) {
754
+ _timeScale = value;
755
+ },
756
+ /**
757
+ * Set the target FPS at runtime.
758
+ *
759
+ * @param {number} fps
760
+ */
761
+ setfps(fps) {
762
+ _step = 1 / fps;
763
+ _stepMs = _step * 1e3;
764
+ _accumulated = 0;
765
+ },
766
+ /**
767
+ * Stops the litecanvas instance and remove all event listeners.
768
+ */
769
+ quit() {
770
+ instance.emit("quit");
771
+ for (const removeListener of _browserEventListeners) {
772
+ removeListener();
773
+ }
774
+ _focused = _events = false;
775
+ if (_global) {
776
+ for (const key in instance) {
777
+ delete root[key];
778
+ }
779
+ delete root.__litecanvas;
780
+ }
781
+ }
782
+ };
783
+ for (const k of [
784
+ "sin",
785
+ "cos",
786
+ "atan2",
787
+ "hypot",
788
+ "tan",
789
+ "abs",
790
+ "ceil",
791
+ "round",
792
+ "floor",
793
+ "trunc",
794
+ "min",
795
+ "max",
796
+ "pow",
797
+ "sqrt",
798
+ "sign",
799
+ "exp"
800
+ ]) {
801
+ instance[k] = Math[k];
802
+ }
803
+ function init() {
804
+ _initialized = true;
805
+ const source = settings.loop ? settings.loop : root;
806
+ for (const event in _events) {
807
+ if (source[event]) instance.listen(event, source[event]);
808
+ }
809
+ for (const [callback, config] of _plugins) {
810
+ loadPlugin(callback, config);
811
+ }
812
+ if (_fullscreen || _autoscale) {
813
+ on(root, "resize", pageResized);
814
+ }
815
+ pageResized();
816
+ if (settings.tapEvents) {
817
+ const _getXY = (pageX, pageY) => [
818
+ (pageX - _canvas.offsetLeft) / _scale,
819
+ (pageY - _canvas.offsetTop) / _scale
820
+ ], _taps = /* @__PURE__ */ new Map(), _registerTap = (id, x, y) => {
821
+ const tap = {
822
+ x,
823
+ y,
824
+ startX: x,
825
+ startY: y,
826
+ // timestamp
827
+ ts: performance.now()
828
+ };
829
+ _taps.set(id, tap);
830
+ return tap;
831
+ }, _updateTap = (id, x, y) => {
832
+ const tap = _taps.get(id) || _registerTap(id);
833
+ tap.x = x;
834
+ tap.y = y;
835
+ }, _checkTapped = (tap) => tap && performance.now() - tap.ts <= 200;
836
+ let _pressingMouse = false;
837
+ on(_canvas, "mousedown", (ev) => {
838
+ ev.preventDefault();
839
+ const [x, y] = _getXY(ev.pageX, ev.pageY);
840
+ instance.emit("tap", x, y, 0);
841
+ _registerTap(0, x, y);
842
+ _pressingMouse = true;
843
+ });
844
+ on(_canvas, "mousemove", (ev) => {
845
+ ev.preventDefault();
846
+ const [x, y] = _getXY(ev.pageX, ev.pageY);
847
+ instance.setvar("MOUSEX", x);
848
+ instance.setvar("MOUSEY", y);
849
+ if (!_pressingMouse) return;
850
+ instance.emit("tapping", x, y, 0);
851
+ _updateTap(0, x, y);
852
+ });
853
+ on(_canvas, "mouseup", (ev) => {
854
+ ev.preventDefault();
855
+ const tap = _taps.get(0);
856
+ const [x, y] = _getXY(ev.pageX, ev.pageY);
857
+ if (_checkTapped(tap)) {
858
+ instance.emit("tapped", tap.startX, tap.startY, 0);
859
+ }
860
+ instance.emit("untap", x, y, 0);
861
+ _taps.delete(0);
862
+ _pressingMouse = false;
863
+ });
864
+ on(_canvas, "touchstart", (ev) => {
865
+ ev.preventDefault();
866
+ const touches = ev.changedTouches;
867
+ for (const touch of touches) {
868
+ const [x, y] = _getXY(touch.pageX, touch.pageY);
869
+ instance.emit("tap", x, y, touch.identifier + 1);
870
+ _registerTap(touch.identifier + 1, x, y);
871
+ }
872
+ });
873
+ on(_canvas, "touchmove", (ev) => {
874
+ ev.preventDefault();
875
+ const touches = ev.changedTouches;
876
+ for (const touch of touches) {
877
+ const [x, y] = _getXY(touch.pageX, touch.pageY);
878
+ instance.emit("tapping", x, y, touch.identifier + 1);
879
+ _updateTap(touch.identifier + 1, x, y);
880
+ }
881
+ });
882
+ const _touchEndHandler = (ev) => {
883
+ ev.preventDefault();
884
+ const existing = [];
885
+ if (ev.targetTouches.length > 0) {
886
+ for (const touch of ev.targetTouches) {
887
+ existing.push(touch.identifier + 1);
888
+ }
889
+ }
890
+ for (const [id, tap] of _taps) {
891
+ if (existing.includes(id)) continue;
892
+ if (_checkTapped(tap)) {
893
+ instance.emit("tapped", tap.startX, tap.startY, id);
894
+ }
895
+ instance.emit("untap", tap.x, tap.y, id);
896
+ _taps.delete(id);
897
+ }
898
+ };
899
+ on(_canvas, "touchend", _touchEndHandler);
900
+ on(_canvas, "touchcancel", _touchEndHandler);
901
+ on(root, "blur", () => {
902
+ _pressingMouse = false;
903
+ for (const [id, tap] of _taps) {
904
+ instance.emit("untap", tap.x, tap.y, id);
905
+ _taps.delete(id);
906
+ }
907
+ });
908
+ }
909
+ if (settings.keyboardEvents) {
910
+ const _keys = /* @__PURE__ */ new Set();
911
+ const iskeydown = (key) => "any" === key ? _keys.size > 0 : _keys.has(key.toLowerCase());
912
+ instance.setvar("iskeydown", iskeydown);
913
+ on(root, "keydown", (event) => {
914
+ _keys.add(event.key.toLowerCase());
915
+ });
916
+ on(root, "keyup", (event) => {
917
+ _keys.delete(event.key.toLowerCase());
918
+ });
919
+ on(root, "blur", () => _keys.clear());
920
+ }
921
+ if (settings.pauseOnBlur) {
922
+ on(root, "blur", () => {
923
+ _focused = false;
924
+ });
925
+ on(root, "focus", () => {
926
+ _focused = true;
927
+ raf(drawFrame);
928
+ });
929
+ }
930
+ instance.setfps(settings.fps);
931
+ instance.emit("init", instance);
932
+ _lastFrame = performance.now();
933
+ raf(drawFrame);
934
+ }
935
+ function drawFrame(now) {
936
+ let shouldRender = !_animated, delta = now - _lastFrame;
937
+ _accumulated += delta > 100 ? _stepMs : delta;
938
+ _lastFrame = now;
939
+ while (_accumulated >= _stepMs) {
940
+ instance.emit("update", _step * _timeScale);
941
+ instance.setvar("ELAPSED", instance.ELAPSED + _step * _timeScale);
942
+ _accumulated -= _stepMs;
943
+ shouldRender = true;
944
+ }
945
+ if (shouldRender) {
946
+ instance.textalign("start", "top");
947
+ instance.emit("draw");
948
+ }
949
+ if (_focused && _animated) {
950
+ raf(drawFrame);
951
+ }
952
+ }
953
+ function setupCanvas() {
954
+ _canvas = "string" === typeof _canvas ? document.querySelector(_canvas) : _canvas;
955
+ instance.setvar("CANVAS", _canvas);
956
+ _ctx = _canvas.getContext("2d");
957
+ on(_canvas, "click", () => root.focus());
958
+ if (instance.WIDTH > 0) {
959
+ _fullscreen = false;
960
+ }
961
+ _canvas.style = "";
962
+ _canvas.width = instance.WIDTH;
963
+ _canvas.height = instance.HEIGHT || instance.WIDTH;
964
+ if (!_canvas.parentNode) document.body.appendChild(_canvas);
965
+ }
966
+ function pageResized() {
967
+ const pageWidth = root.innerWidth, pageHeight = root.innerHeight, styles = _canvas.style;
968
+ styles.display = "block";
969
+ if (_fullscreen) {
970
+ styles.position = "absolute";
971
+ styles.inset = 0;
972
+ instance.setvar("WIDTH", _canvas.width = pageWidth);
973
+ instance.setvar("HEIGHT", _canvas.height = pageHeight);
974
+ } else if (_autoscale) {
975
+ styles.margin = "auto";
976
+ _scale = Math.min(
977
+ pageWidth / instance.WIDTH,
978
+ pageHeight / instance.HEIGHT
979
+ );
980
+ _scale = (settings.pixelart ? ~~_scale : _scale) || 1;
981
+ styles.width = instance.WIDTH * _scale + "px";
982
+ styles.height = instance.HEIGHT * _scale + "px";
983
+ }
984
+ instance.setvar("CENTERX", instance.WIDTH / 2);
985
+ instance.setvar("CENTERY", instance.HEIGHT / 2);
986
+ if (!settings.antialias || settings.pixelart) {
987
+ _ctx.imageSmoothingEnabled = false;
988
+ _canvas.style.imageRendering = "pixelated";
989
+ }
990
+ instance.emit("resized", _scale);
991
+ if (!_animated) {
992
+ raf(drawFrame);
993
+ }
994
+ }
995
+ function triggerEvent(eventName, arg1, arg2, arg3, arg4) {
996
+ if (!_events[eventName]) return;
997
+ for (const callback of _events[eventName]) {
998
+ callback(arg1, arg2, arg3, arg4);
999
+ }
1000
+ }
1001
+ function loadPlugin(callback, config) {
1002
+ const pluginData = callback(instance, _helpers, config);
1003
+ if ("object" === typeof pluginData) {
1004
+ for (const key of Object.keys(pluginData)) {
1005
+ instance.setvar(key, pluginData[key]);
1006
+ }
1007
+ }
1008
+ }
1009
+ if (_global) {
1010
+ if (root.__litecanvas) {
1011
+ throw "global litecanvas already instantiated";
1012
+ }
1013
+ Object.assign(root, instance);
1014
+ root.__litecanvas = instance;
1015
+ }
1016
+ setupCanvas();
1017
+ if ("loading" === document.readyState) {
1018
+ on(root, "DOMContentLoaded", () => raf(init));
1019
+ } else {
1020
+ raf(init);
1021
+ }
1022
+ return instance;
1023
+ }
1024
+
59
1025
  // src/actor/index.js
60
1026
  var ANCHOR_CENTER = /* @__PURE__ */ vec(0.5, 0.5);
61
1027
  var ANCHOR_TOP_LEFT = /* @__PURE__ */ vec(0, 0);
@@ -182,20 +1148,20 @@
182
1148
  *
183
1149
  * @param {LitecanvasInstance} [litecanvas]
184
1150
  */
185
- draw(litecanvas = globalThis, saveContext = true) {
1151
+ draw(litecanvas2 = globalThis, saveContext = true) {
186
1152
  if (this.hidden || this.opacity <= 0) return;
187
- if (saveContext) litecanvas.push();
188
- this.transform(litecanvas);
189
- this.drawImage(litecanvas);
190
- if (saveContext) litecanvas.pop();
1153
+ if (saveContext) litecanvas2.push();
1154
+ this.transform(litecanvas2);
1155
+ this.drawImage(litecanvas2);
1156
+ if (saveContext) litecanvas2.pop();
191
1157
  }
192
1158
  /**
193
1159
  * @param {LitecanvasInstance} litecanvas
194
1160
  */
195
- transform(litecanvas) {
196
- litecanvas.translate(this.pos.x, this.pos.y);
197
- litecanvas.rotate(litecanvas.deg2rad(this.angle));
198
- litecanvas.scale(
1161
+ transform(litecanvas2) {
1162
+ litecanvas2.translate(this.pos.x, this.pos.y);
1163
+ litecanvas2.rotate(litecanvas2.deg2rad(this.angle));
1164
+ litecanvas2.scale(
199
1165
  (this.flipX ? -1 : 1) * this._s.x,
200
1166
  (this.flipY ? -1 : 1) * this._s.y
201
1167
  );
@@ -203,12 +1169,12 @@
203
1169
  /**
204
1170
  * @param {LitecanvasInstance} litecanvas
205
1171
  */
206
- drawImage(litecanvas, alpha = true) {
1172
+ drawImage(litecanvas2, alpha = true) {
207
1173
  const anchor = this.anchor;
208
1174
  const x = -this.sprite.width * (this.flipX ? 1 - anchor.x : anchor.x);
209
1175
  const y = -this.sprite.height * (this.flipY ? 1 - anchor.y : anchor.y);
210
- if (alpha) litecanvas.alpha(this.opacity);
211
- litecanvas.image(x, y, this.sprite);
1176
+ if (alpha) litecanvas2.alpha(this.opacity);
1177
+ litecanvas2.image(x, y, this.sprite);
212
1178
  }
213
1179
  };
214
1180