littlejsengine 1.7.12 → 1.7.21

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.
@@ -1,5 +1,7 @@
1
1
  // LittleJS - MIT License - Copyright 2021 Frank Force
2
2
 
3
+ 'use strict';
4
+
3
5
  /**
4
6
  * LittleJS Debug System
5
7
  * - Press Esc to show debug overlay with mouse pick
@@ -10,7 +12,7 @@
10
12
  * @namespace Debug
11
13
  */
12
14
 
13
- 'use strict';
15
+
14
16
 
15
17
  /** True if debug is enabled
16
18
  * @type {Boolean}
@@ -213,7 +215,11 @@ function debugRender()
213
215
  mainContext.drawImage(overlayCanvas, 0, 0);
214
216
  overlayCanvas.width |= 0;
215
217
 
216
- debugSaveCanvas(mainCanvas);
218
+ // remove alpha and save
219
+ const w = mainCanvas.width, h = mainCanvas.height;
220
+ overlayContext.fillRect(0,0,w,h);
221
+ overlayContext.drawImage(mainCanvas, 0, 0);
222
+ debugSaveCanvas(overlayCanvas);
217
223
  debugTakeScreenshot = 0;
218
224
  }
219
225
 
@@ -414,7 +420,7 @@ function debugRender()
414
420
  * @namespace Utilities
415
421
  */
416
422
 
417
- 'use strict';
423
+
418
424
 
419
425
  /** A shortcut to get Math.PI
420
426
  * @type {Number}
@@ -1069,7 +1075,7 @@ class Timer
1069
1075
  * @namespace Settings
1070
1076
  */
1071
1077
 
1072
- 'use strict';
1078
+
1073
1079
 
1074
1080
  ///////////////////////////////////////////////////////////////////////////////
1075
1081
  // Camera settings
@@ -1317,7 +1323,7 @@ let medalsPreventUnlock;
1317
1323
  * LittleJS Object System
1318
1324
  */
1319
1325
 
1320
- 'use strict';
1326
+
1321
1327
 
1322
1328
  /**
1323
1329
  * LittleJS Object Base Object Class
@@ -1713,7 +1719,7 @@ class EngineObject
1713
1719
  * @namespace Draw
1714
1720
  */
1715
1721
 
1716
- 'use strict';
1722
+
1717
1723
 
1718
1724
  /** The primary 2D canvas visible to the user
1719
1725
  * @type {HTMLCanvasElement}
@@ -1884,7 +1890,7 @@ function drawPoly(points, color=new Color, useWebGL=glEnable, screenSpace)
1884
1890
  * @param {Boolean} [useWebGL=glEnable]
1885
1891
  * @param {Boolean} [screenSpace=0]
1886
1892
  * @memberof Draw */
1887
- function drawLine(posA, posB, thickness=.1, color, useWebGL)
1893
+ function drawLine(posA, posB, thickness=.1, color, useWebGL, screenSpace)
1888
1894
  {
1889
1895
  const halfDelta = vec2((posB.x - posA.x)/2, (posB.y - posA.y)/2);
1890
1896
  const size = vec2(thickness, halfDelta.length()*2);
@@ -1904,12 +1910,12 @@ function drawCanvas2D(pos, size, angle, mirror, drawFunction, context = mainCont
1904
1910
  {
1905
1911
  if (!screenSpace)
1906
1912
  {
1907
- // create canvas transform from world space to screen space
1913
+ // transform from world space to screen space
1908
1914
  pos = worldToScreen(pos);
1909
1915
  size = size.scale(cameraScale);
1910
1916
  }
1911
1917
  context.save();
1912
- context.translate(pos.x+.5|0, pos.y+.5|0);
1918
+ context.translate(pos.x+.5, pos.y+.5);
1913
1919
  context.rotate(angle);
1914
1920
  context.scale(mirror ? -size.x : size.x, size.y);
1915
1921
  drawFunction(context);
@@ -2094,7 +2100,7 @@ function toggleFullscreen()
2094
2100
  * @namespace Input
2095
2101
  */
2096
2102
 
2097
- 'use strict';
2103
+
2098
2104
 
2099
2105
  /** Returns true if device key is down
2100
2106
  * @param {Number} key
@@ -2278,18 +2284,26 @@ function mouseToScreen(mousePos)
2278
2284
  const stickData = [];
2279
2285
  function gamepadsUpdate()
2280
2286
  {
2281
- if (touchGamepadEnable && touchGamepadTimer.isSet())
2287
+ // update touch gamepad if enabled
2288
+ if (touchGamepadEnable && isTouchDevice)
2282
2289
  {
2283
- // read virtual analog stick
2284
- const sticks = stickData[0] || (stickData[0] = []);
2285
- sticks[0] = vec2(touchGamepadStick.x, -touchGamepadStick.y); // flip vertical
2290
+ // create the touch gamepad if it doesn't exist
2291
+ if (!touchGamepadButtons)
2292
+ createTouchGamepad();
2286
2293
 
2287
- // read virtual gamepad buttons
2288
- const data = inputData[1] || (inputData[1] = []);
2289
- for (let i=10; i--;)
2294
+ if (touchGamepadTimer.isSet())
2290
2295
  {
2291
- const j = i == 3 ? 2 : i == 2 ? 3 : i; // fix button locations
2292
- data[j] = touchGamepadButtons[i] ? 1 + 2*!gamepadIsDown(j,0) : 4*gamepadIsDown(j,0);
2296
+ // read virtual analog stick
2297
+ const sticks = stickData[0] || (stickData[0] = []);
2298
+ sticks[0] = vec2(touchGamepadStick.x, -touchGamepadStick.y); // flip vertical
2299
+
2300
+ // read virtual gamepad buttons
2301
+ const data = inputData[1] || (inputData[1] = []);
2302
+ for (let i=10; i--;)
2303
+ {
2304
+ const j = i == 3 ? 2 : i == 2 ? 3 : i; // fix button locations
2305
+ data[j] = touchGamepadButtons[i] ? 1 + 2*!gamepadIsDown(j,0) : 4*gamepadIsDown(j,0);
2306
+ }
2293
2307
  }
2294
2308
  }
2295
2309
 
@@ -2386,6 +2400,10 @@ if (isTouchDevice)
2386
2400
  // set was touching
2387
2401
  wasTouching = touching;
2388
2402
 
2403
+ // prevent default handling like copy and magnifier lens
2404
+ if (document.hasFocus()) // allow document to get focus
2405
+ e.preventDefault();
2406
+
2389
2407
  // must return true so the document will get focus
2390
2408
  return true;
2391
2409
  }
@@ -2398,7 +2416,7 @@ if (isTouchDevice)
2398
2416
  let touchGamepadTimer = new Timer, touchGamepadButtons, touchGamepadStick;
2399
2417
 
2400
2418
  // create the touch gamepad, called automatically by the engine
2401
- if (touchGamepadEnable)
2419
+ function createTouchGamepad()
2402
2420
  {
2403
2421
  // touch input internal variables
2404
2422
  touchGamepadButtons = [];
@@ -2532,7 +2550,7 @@ function touchGamepadRender()
2532
2550
  * @namespace Audio
2533
2551
  */
2534
2552
 
2535
- 'use strict';
2553
+
2536
2554
 
2537
2555
  /**
2538
2556
  * Sound Object - Stores a zzfx sound for later use and can be played positionally
@@ -2568,8 +2586,9 @@ class Sound
2568
2586
  if (zzfxSound)
2569
2587
  {
2570
2588
  // generate zzfx sound now for fast playback
2571
- this.randomness = zzfxSound[1] || (zzfxSound[1] = 0);
2572
- this.cachedSamples = zzfxSound && zzfxG(...zzfxSound);
2589
+ this.randomness = zzfxSound[1] || (zzfxSound[1] = 0);
2590
+ this.sampleChannels = [zzfxG(...zzfxSound)];
2591
+ this.sampleRate = zzfxR;
2573
2592
  }
2574
2593
  }
2575
2594
 
@@ -2578,11 +2597,12 @@ class Sound
2578
2597
  * @param {Number} [volume=1] - How much to scale volume by (in addition to range fade)
2579
2598
  * @param {Number} [pitch=1] - How much to scale pitch by (also adjusted by this.randomness)
2580
2599
  * @param {Number} [randomnessScale=1] - How much to scale randomness
2581
- * @return {AudioBufferSourceNode} - The audio, can be used to stop sound later
2600
+ * @param {Boolean} [loop=0] - Should the sound loop
2601
+ * @return {AudioBufferSourceNode} - The audio source node
2582
2602
  */
2583
- play(pos, volume=1, pitch=1, randomnessScale=1)
2603
+ play(pos, volume=1, pitch=1, randomnessScale=1, loop=0)
2584
2604
  {
2585
- if (!soundEnable || !this.cachedSamples) return;
2605
+ if (!soundEnable || !this.sampleChannels) return;
2586
2606
 
2587
2607
  let pan;
2588
2608
  if (pos)
@@ -2605,43 +2625,80 @@ class Sound
2605
2625
 
2606
2626
  // play the sound
2607
2627
  const playbackRate = pitch + pitch * this.randomness*randomnessScale*rand(-1,1);
2608
- return playSamples([this.cachedSamples], volume, playbackRate, pan);
2628
+ return this.source = playSamples(this.sampleChannels, volume, playbackRate, pan, loop, this.sampleRate);
2629
+ }
2630
+
2631
+ /** Stop the last instance of this sound that was played */
2632
+ stop()
2633
+ {
2634
+ if (this.source)
2635
+ this.source.stop();
2636
+ this.source = 0;
2609
2637
  }
2610
2638
 
2611
2639
  /** Play the sound as a note with a semitone offset
2612
2640
  * @param {Number} semitoneOffset - How many semitones to offset pitch
2613
2641
  * @param {Vector2} [pos] - World space position to play the sound, sound is not attenuated if null
2614
2642
  * @param {Number} [volume=1] - How much to scale volume by (in addition to range fade)
2615
- * @return {AudioBufferSourceNode} - The audio, can be used to stop sound later
2643
+ * @return {AudioBufferSourceNode} - The audio source node
2616
2644
  */
2617
2645
  playNote(semitoneOffset, pos, volume)
2618
2646
  { return this.play(pos, volume, 2**(semitoneOffset/12), 0); }
2647
+
2648
+ /** Get how long this sound is in seconds
2649
+ * @return {Number} - How long the sound is in seconds (undefined if loading)
2650
+ */
2651
+ getDuration()
2652
+ { return this.sampleChannels && this.sampleChannels[0].length / this.sampleRate; }
2653
+
2654
+ /** Check if the last instance of this sound is playing
2655
+ * @return {Boolean} - True if the sound is playing
2656
+ */
2657
+ isPlaying() { return this.source && !this.source.ended; }
2658
+
2659
+ /** Check if sound is loading, for sounds fetched from a url
2660
+ * @return {Boolean} - True if sound is loading and not ready to play
2661
+ */
2662
+ isLoading() { return !this.sampleChannels; }
2619
2663
  }
2620
2664
 
2621
2665
  /**
2622
2666
  * Sound Wave Object - Stores a wave sound for later use and can be played positionally
2667
+ * - this can be used to play wave, mp3, and ogg files
2668
+ * @example
2669
+ * // create a sound
2670
+ * const sound_example = new SoundWave('sound.mp3');
2671
+ *
2672
+ * // play the sound
2673
+ * sound_example.play();
2623
2674
  */
2624
2675
  class SoundWave extends Sound
2625
2676
  {
2626
2677
  /** Create a sound object and cache the wave file for later use
2627
- * @param {String} waveFilename - Filename of wave file to load
2628
- * @param {Number} [randomness=.05] - How much to randomize frequency each time sound plays
2678
+ * @param {String} filename - Filename of audio file to load
2679
+ * @param {Number} [randomness=0] - How much to randomize frequency each time sound plays
2629
2680
  * @param {Number} [range=soundDefaultRange] - World space max range of sound, will not play if camera is farther away
2630
2681
  * @param {Number} [taper=soundDefaultTaper] - At what percentage of range should it start tapering off
2631
2682
  */
2632
- constructor(waveFilename, randomness=.05, range, taper)
2683
+ constructor(filename, randomness=0, range, taper)
2633
2684
  {
2634
2685
  super(0, range, taper);
2635
2686
  this.randomness = randomness;
2636
2687
 
2637
2688
  if (!soundEnable) return;
2638
- if (!soundWaveDecoderContext)
2689
+ if (!soundDecoderContext)
2639
2690
  soundDecoderContext = new AudioContext;
2640
2691
 
2641
- fetch(waveFilename)
2692
+ fetch(filename)
2642
2693
  .then(response => response.arrayBuffer())
2643
- .then(arrayBuffer => soundWaveDecoderContext.decodeAudioData(arrayBuffer))
2644
- .then(audioBuffer => this.cachedSamples = audioBuffer.getChannelData(0));
2694
+ .then(arrayBuffer => soundDecoderContext.decodeAudioData(arrayBuffer))
2695
+ .then(audioBuffer =>
2696
+ {
2697
+ this.sampleChannels = [];
2698
+ for (let i = audioBuffer.numberOfChannels; i--;)
2699
+ this.sampleChannels[i] = audioBuffer.getChannelData(i);
2700
+ this.sampleRate = audioBuffer.sampleRate;
2701
+ });
2645
2702
  }
2646
2703
  }
2647
2704
  let soundDecoderContext; // audio context used only to decode audio files
@@ -2676,48 +2733,34 @@ let soundDecoderContext; // audio context used only to decode audio files
2676
2733
  * // play the music
2677
2734
  * music_example.play();
2678
2735
  */
2679
- class Music
2736
+ class Music extends Sound
2680
2737
  {
2681
2738
  /** Create a music object and cache the zzfx music samples for later use
2682
2739
  * @param {Array} zzfxMusic - Array of zzfx music parameters
2683
2740
  */
2684
2741
  constructor(zzfxMusic)
2685
2742
  {
2686
- if (!soundEnable) return;
2743
+ super();
2687
2744
 
2688
- this.cachedSamples = zzfxM(...zzfxMusic);
2745
+ if (!soundEnable) return;
2746
+ this.randomness = 0;
2747
+ this.sampleChannels = zzfxM(...zzfxMusic);
2748
+ this.sampleRate = zzfxR;
2689
2749
  }
2690
2750
 
2691
2751
  /** Play the music
2692
2752
  * @param {Number} [volume=1] - How much to scale volume by
2693
- * @param {Boolean} [loop=1] - True if the music should loop when it reaches the end
2694
- * @return {AudioBufferSourceNode} - The audio node, can be used to stop sound later
2753
+ * @param {Boolean} [loop=1] - True if the music should loop
2754
+ * @return {AudioBufferSourceNode} - The audio source node
2695
2755
  */
2696
2756
  play(volume, loop = 1)
2697
- {
2698
- if (!soundEnable) return;
2699
-
2700
- return this.source = playSamples(this.cachedSamples, volume, 1, 0, loop);
2701
- }
2702
-
2703
- /** Stop the music */
2704
- stop()
2705
- {
2706
- if (this.source)
2707
- this.source.stop();
2708
- this.source = 0;
2709
- }
2710
-
2711
- /** Check if music is playing
2712
- * @return {Boolean}
2713
- */
2714
- isPlaying() { return this.source; }
2757
+ { return super.play(0, volume, 1, 1, loop); }
2715
2758
  }
2716
2759
 
2717
- /** Play an mp3 or wav audio from a local file or url
2760
+ /** Play an mp3, ogg, or wav audio from a local file or url
2718
2761
  * @param {String} url - Location of sound file to play
2719
2762
  * @param {Number} [volume=1] - How much to scale volume by
2720
- * @param {Boolean} [loop=1] - True if the music should loop when it reaches the end
2763
+ * @param {Boolean} [loop=1] - True if the music should loop
2721
2764
  * @return {HTMLAudioElement} - The audio element for this sound
2722
2765
  * @memberof Audio */
2723
2766
  function playAudioFile(url, volume=1, loop=1)
@@ -2781,9 +2824,10 @@ let audioContext;
2781
2824
  * @param {Number} [rate=1] - The playback rate to use
2782
2825
  * @param {Number} [pan=0] - How much to apply stereo panning
2783
2826
  * @param {Boolean} [loop=0] - True if the sound should loop when it reaches the end
2827
+ * @param {Number} [sampleRate=44100] - Sample rate for the sound
2784
2828
  * @return {AudioBufferSourceNode} - The audio node of the sound played
2785
2829
  * @memberof Audio */
2786
- function playSamples(sampleChannels, volume=1, rate=1, pan=0, loop=0)
2830
+ function playSamples(sampleChannels, volume=1, rate=1, pan=0, loop=0, sampleRate=zzfxR)
2787
2831
  {
2788
2832
  if (!soundEnable) return;
2789
2833
 
@@ -2800,7 +2844,7 @@ function playSamples(sampleChannels, volume=1, rate=1, pan=0, loop=0)
2800
2844
  }
2801
2845
 
2802
2846
  // create buffer and source
2803
- const buffer = audioContext.createBuffer(sampleChannels.length, sampleChannels[0].length, zzfxR),
2847
+ const buffer = audioContext.createBuffer(sampleChannels.length, sampleChannels[0].length, sampleRate),
2804
2848
  source = audioContext.createBufferSource();
2805
2849
 
2806
2850
  // copy samples to buffer and setup source
@@ -3059,7 +3103,7 @@ function zzfxM(instruments, patterns, sequence, BPM = 125)
3059
3103
  * @namespace TileCollision
3060
3104
  */
3061
3105
 
3062
- 'use strict';
3106
+
3063
3107
 
3064
3108
  /** The tile collision layer array, use setTileCollisionData and getTileCollisionData to access
3065
3109
  * @type {Array}
@@ -3412,7 +3456,7 @@ constructor(pos, size=tileCollisionSize, tileSize=tileSizeDefault, scale=vec2(1)
3412
3456
  * LittleJS Particle System
3413
3457
  */
3414
3458
 
3415
- 'use strict';
3459
+
3416
3460
 
3417
3461
  /**
3418
3462
  * Particle Emitter - Spawns particles with the given settings
@@ -3722,7 +3766,7 @@ class Particle extends EngineObject
3722
3766
  * @namespace Medals
3723
3767
  */
3724
3768
 
3725
- 'use strict';
3769
+
3726
3770
 
3727
3771
  /** List of all medals
3728
3772
  * @type {Array}
@@ -4046,7 +4090,7 @@ class Newgrounds
4046
4090
  * @namespace WebGL
4047
4091
  */
4048
4092
 
4049
- 'use strict';
4093
+
4050
4094
 
4051
4095
  /** The WebGL canvas which appears above the main canvas and below the overlay canvas
4052
4096
  * @type {HTMLCanvasElement}
@@ -4068,7 +4112,7 @@ let glActiveTexture, glShader, glArrayBuffer, glPositionData, glColorData, glBat
4068
4112
 
4069
4113
  ///////////////////////////////////////////////////////////////////////////////
4070
4114
 
4071
- // Init WebGL, called automatically by the engine
4115
+ // Initalize WebGL, called automatically by the engine
4072
4116
  function glInit()
4073
4117
  {
4074
4118
  // create the canvas and tile texture
@@ -4107,6 +4151,48 @@ function glInit()
4107
4151
  glBatchCount = 0;
4108
4152
  }
4109
4153
 
4154
+ // Setup render each frame, called automatically by engine
4155
+ function glPreRender()
4156
+ {
4157
+ // clear and set to same size as main canvas
4158
+ glContext.viewport(0, 0, glCanvas.width = mainCanvas.width, glCanvas.height = mainCanvas.height);
4159
+ glContext.clear(gl_COLOR_BUFFER_BIT);
4160
+
4161
+ // set up the shader
4162
+ glContext.useProgram(glShader);
4163
+ glContext.activeTexture(gl_TEXTURE0);
4164
+ glContext.bindTexture(gl_TEXTURE_2D, glActiveTexture = glTileTexture);
4165
+ glContext.bindBuffer(gl_ARRAY_BUFFER, glArrayBuffer);
4166
+ glContext.bufferData(gl_ARRAY_BUFFER, gl_VERTEX_BUFFER_SIZE, gl_DYNAMIC_DRAW);
4167
+ glSetBlendMode();
4168
+
4169
+ // set vertex attributes
4170
+ let offset = 0;
4171
+ const initVertexAttribArray = (name, type, typeSize, size, normalize=0)=>
4172
+ {
4173
+ const location = glContext.getAttribLocation(glShader, name);
4174
+ glContext.enableVertexAttribArray(location);
4175
+ glContext.vertexAttribPointer(location, size, type, normalize, gl_VERTEX_BYTE_STRIDE, offset);
4176
+ offset += size*typeSize;
4177
+ }
4178
+ initVertexAttribArray('p', gl_FLOAT, 4, 2); // position
4179
+ initVertexAttribArray('t', gl_FLOAT, 4, 2); // texture coords
4180
+ initVertexAttribArray('c', gl_UNSIGNED_BYTE, 1, 4, 1); // color
4181
+ initVertexAttribArray('a', gl_UNSIGNED_BYTE, 1, 4, 1); // additiveColor
4182
+
4183
+ // build the transform matrix
4184
+ const sx = 2 * cameraScale / mainCanvas.width;
4185
+ const sy = 2 * cameraScale / mainCanvas.height;
4186
+ glContext.uniformMatrix4fv(glContext.getUniformLocation(glShader, 'm'), 0,
4187
+ new Float32Array([
4188
+ sx, 0, 0, 0,
4189
+ 0, sy, 0, 0,
4190
+ 1, 1, -1, 1,
4191
+ -1-sx*cameraPos.x, -1-sy*cameraPos.y, 0, 0
4192
+ ])
4193
+ );
4194
+ }
4195
+
4110
4196
  /** Set the WebGl blend mode, normally you should call setBlendMode instead
4111
4197
  * @param {Boolean} [additive=0]
4112
4198
  * @memberof WebGL */
@@ -4186,48 +4272,6 @@ function glCreateTexture(image)
4186
4272
  return texture;
4187
4273
  }
4188
4274
 
4189
- // called automatically by engine before render
4190
- function glPreRender()
4191
- {
4192
- // clear and set to same size as main canvas
4193
- glContext.viewport(0, 0, glCanvas.width = mainCanvas.width, glCanvas.height = mainCanvas.height);
4194
- glContext.clear(gl_COLOR_BUFFER_BIT);
4195
-
4196
- // set up the shader
4197
- glContext.useProgram(glShader);
4198
- glContext.activeTexture(gl_TEXTURE0);
4199
- glContext.bindTexture(gl_TEXTURE_2D, glActiveTexture = glTileTexture);
4200
- glContext.bindBuffer(gl_ARRAY_BUFFER, glArrayBuffer);
4201
- glContext.bufferData(gl_ARRAY_BUFFER, gl_VERTEX_BUFFER_SIZE, gl_DYNAMIC_DRAW);
4202
- glSetBlendMode();
4203
-
4204
- // set vertex attributes
4205
- let offset = 0;
4206
- const initVertexAttribArray = (name, type, typeSize, size, normalize=0)=>
4207
- {
4208
- const location = glContext.getAttribLocation(glShader, name);
4209
- glContext.enableVertexAttribArray(location);
4210
- glContext.vertexAttribPointer(location, size, type, normalize, gl_VERTEX_BYTE_STRIDE, offset);
4211
- offset += size*typeSize;
4212
- }
4213
- initVertexAttribArray('p', gl_FLOAT, 4, 2); // position
4214
- initVertexAttribArray('t', gl_FLOAT, 4, 2); // texture coords
4215
- initVertexAttribArray('c', gl_UNSIGNED_BYTE, 1, 4, 1); // color
4216
- initVertexAttribArray('a', gl_UNSIGNED_BYTE, 1, 4, 1); // additiveColor
4217
-
4218
- // build the transform matrix
4219
- const sx = 2 * cameraScale / mainCanvas.width;
4220
- const sy = 2 * cameraScale / mainCanvas.height;
4221
- glContext.uniformMatrix4fv(glContext.getUniformLocation(glShader, 'm'), 0,
4222
- new Float32Array([
4223
- sx, 0, 0, 0,
4224
- 0, sy, 0, 0,
4225
- 1, 1, -1, 1,
4226
- -1-sx*cameraPos.x, -1-sy*cameraPos.y, 0, 0
4227
- ])
4228
- );
4229
- }
4230
-
4231
4275
  /** Draw all sprites and clear out the buffer, called automatically by the system whenever necessary
4232
4276
  * @memberof WebGL */
4233
4277
  function glFlush()
@@ -4485,7 +4529,7 @@ gl_VERTEX_BUFFER_SIZE = gl_MAX_BATCH * gl_VERTEX_BYTE_STRIDE;
4485
4529
  * @namespace Engine
4486
4530
  */
4487
4531
 
4488
- 'use strict';
4532
+
4489
4533
 
4490
4534
  /** Name of engine
4491
4535
  * @type {String}
@@ -4497,7 +4541,7 @@ const engineName = 'LittleJS';
4497
4541
  * @type {String}
4498
4542
  * @default
4499
4543
  * @memberof Engine */
4500
- const engineVersion = '1.7.12';
4544
+ const engineVersion = '1.7.21';
4501
4545
 
4502
4546
  /** Frames per second to update objects
4503
4547
  * @type {Number}
@@ -4567,11 +4611,13 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
4567
4611
  debug && (tileImage.onload=()=>ASSERT(1)); // tile sheet can not reloaded
4568
4612
 
4569
4613
  // setup html
4570
- const styleBody = 'margin:0;overflow:hidden;' + // fill the window
4571
- 'background:#000;' + // set background color
4572
- 'touch-action:none;' + // prevent mobile pinch to resize
4573
- 'user-select:none;' + // prevent mobile hold to select
4574
- '-webkit-user-select:none'; // compatibility for ios
4614
+ const styleBody =
4615
+ 'margin:0;overflow:hidden;' + // fill the window
4616
+ 'background:#000;' + // set background color
4617
+ 'touch-action:none;' + // prevent mobile pinch to resize
4618
+ 'user-select:none;' + // prevent mobile hold to select
4619
+ '-webkit-user-select:none;' + // compatibility for ios
4620
+ '-webkit-touch-callout:none'; // compatibility for ios
4575
4621
  document.body.style = styleBody;
4576
4622
  document.body.appendChild(mainCanvas = document.createElement('canvas'));
4577
4623
  mainContext = mainCanvas.getContext('2d');
@@ -4585,7 +4631,8 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
4585
4631
  overlayContext = overlayCanvas.getContext('2d');
4586
4632
 
4587
4633
  // set canvas style
4588
- const styleCanvas = 'position:absolute;' +
4634
+ const styleCanvas =
4635
+ 'position:absolute;' + // position canvas
4589
4636
  'top:50%;left:50%;transform:translate(-50%,-50%);' + // center the canvas
4590
4637
  (canvasPixelated?'image-rendering:pixelated':''); // set pixelated rendering
4591
4638
  (glCanvas||mainCanvas).style = mainCanvas.style = overlayCanvas.style = styleCanvas;