littlejsengine 1.9.2 → 1.9.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.
- package/README.md +4 -2
- package/dist/littlejs.d.ts +31 -26
- package/dist/littlejs.esm.js +145 -122
- package/dist/littlejs.esm.min.js +1 -1
- package/dist/littlejs.js +145 -122
- package/dist/littlejs.min.js +1 -1
- package/dist/littlejs.release.js +140 -121
- package/examples/logo.png +0 -0
- package/examples/module/game.js +1 -1
- package/examples/platformer/data/{gameTileData.tmx → gameLevelData.tmx} +3 -3
- package/examples/platformer/game.js +31 -10
- package/examples/platformer/gameCharacter.js +7 -6
- package/examples/platformer/gameEffects.js +77 -68
- package/examples/platformer/gameLevel.js +33 -17
- package/examples/platformer/{gameTileData.js → gameLevelData.js} +3 -3
- package/examples/platformer/gameObjects.js +14 -14
- package/examples/platformer/index.html +1 -1
- package/examples/platformer/tiles.png +0 -0
- package/examples/screenshot.jpg +0 -0
- package/examples/stress/index.html +1 -1
- package/examples/typescript/game.js +1 -1
- package/examples/typescript/game.ts +1 -1
- package/package.json +1 -1
- package/reference.md +409 -0
- package/src/engine.js +47 -42
- package/src/engineAudio.js +19 -10
- package/src/engineDebug.js +5 -1
- package/src/engineDraw.js +19 -23
- package/src/engineInput.js +31 -17
- package/src/engineMedals.js +1 -1
- package/src/engineObject.js +2 -2
- package/src/engineParticles.js +11 -10
- package/src/engineSettings.js +3 -3
- package/src/engineTileLayer.js +2 -2
- package/src/engineUtilities.js +4 -4
- package/src/engineWebGL.js +1 -7
- /package/examples/platformer/data/{gameTileData.tsx → gameLevelData.tsx} +0 -0
package/dist/littlejs.release.js
CHANGED
|
@@ -71,7 +71,7 @@ function min(valueA, valueB) { return Math.min(valueA, valueB); }
|
|
|
71
71
|
* @memberof Utilities */
|
|
72
72
|
function max(valueA, valueB) { return Math.max(valueA, valueB); }
|
|
73
73
|
|
|
74
|
-
/** Returns the sign of value passed in
|
|
74
|
+
/** Returns the sign of value passed in
|
|
75
75
|
* @param {Number} value
|
|
76
76
|
* @return {Number}
|
|
77
77
|
* @memberof Utilities */
|
|
@@ -118,7 +118,7 @@ function lerp(percent, valueA, valueB) { return valueA + clamp(percent) * (value
|
|
|
118
118
|
function distanceWrap(valueA, valueB, wrapSize=1)
|
|
119
119
|
{ const d = (valueA - valueB) % wrapSize; return d*2 % wrapSize - d; }
|
|
120
120
|
|
|
121
|
-
/** Linearly interpolates between values passed in with
|
|
121
|
+
/** Linearly interpolates between values passed in with wrapping
|
|
122
122
|
* @param {Number} percent
|
|
123
123
|
* @param {Number} valueA
|
|
124
124
|
* @param {Number} valueB
|
|
@@ -135,7 +135,7 @@ function lerpWrap(percent, valueA, valueB, wrapSize=1)
|
|
|
135
135
|
* @memberof Utilities */
|
|
136
136
|
function distanceAngle(angleA, angleB) { return distanceWrap(angleA, angleB, 2*PI); }
|
|
137
137
|
|
|
138
|
-
/** Linearly interpolates between the angles passed in with
|
|
138
|
+
/** Linearly interpolates between the angles passed in with wrapping
|
|
139
139
|
* @param {Number} percent
|
|
140
140
|
* @param {Number} angleA
|
|
141
141
|
* @param {Number} angleB
|
|
@@ -665,7 +665,7 @@ class Color
|
|
|
665
665
|
|
|
666
666
|
/** Returns this color expressed in hsla format
|
|
667
667
|
* @return {Array} */
|
|
668
|
-
|
|
668
|
+
HSLA()
|
|
669
669
|
{
|
|
670
670
|
const r = clamp(this.r);
|
|
671
671
|
const g = clamp(this.g);
|
|
@@ -877,7 +877,7 @@ let tileSizeDefault = vec2(16);
|
|
|
877
877
|
* @type {Number}
|
|
878
878
|
* @default
|
|
879
879
|
* @memberof Settings */
|
|
880
|
-
let tileFixBleedScale = .
|
|
880
|
+
let tileFixBleedScale = .1;
|
|
881
881
|
|
|
882
882
|
///////////////////////////////////////////////////////////////////////////////
|
|
883
883
|
// Object settings
|
|
@@ -888,7 +888,7 @@ let tileFixBleedScale = .3;
|
|
|
888
888
|
* @memberof Settings */
|
|
889
889
|
let enablePhysicsSolver = true;
|
|
890
890
|
|
|
891
|
-
/** Default object mass for
|
|
891
|
+
/** Default object mass for collision calcuations (how heavy objects are)
|
|
892
892
|
* @type {Number}
|
|
893
893
|
* @default
|
|
894
894
|
* @memberof Settings */
|
|
@@ -971,7 +971,7 @@ let touchGamepadEnable = false;
|
|
|
971
971
|
* @memberof Settings */
|
|
972
972
|
let touchGamepadAnalog = true;
|
|
973
973
|
|
|
974
|
-
/** Size of
|
|
974
|
+
/** Size of virtual gamepad for touch devices in pixels
|
|
975
975
|
* @type {Number}
|
|
976
976
|
* @default
|
|
977
977
|
* @memberof Settings */
|
|
@@ -1258,7 +1258,7 @@ function setDebugKey(key) { debugKey = key; }
|
|
|
1258
1258
|
* - Automatically adds self to object list
|
|
1259
1259
|
* - Will be updated and rendered each frame
|
|
1260
1260
|
* - Renders as a sprite from a tilesheet by default
|
|
1261
|
-
* - Can have color and
|
|
1261
|
+
* - Can have color and additive color applied
|
|
1262
1262
|
* - 2D Physics and collision system
|
|
1263
1263
|
* - Sorted by renderOrder
|
|
1264
1264
|
* - Objects can have children attached
|
|
@@ -1537,7 +1537,7 @@ class EngineObject
|
|
|
1537
1537
|
}
|
|
1538
1538
|
|
|
1539
1539
|
/** Destroy this object, destroy it's children, detach it's parent, and mark it for removal */
|
|
1540
|
-
destroy()
|
|
1540
|
+
destroy()
|
|
1541
1541
|
{
|
|
1542
1542
|
if (this.destroyed)
|
|
1543
1543
|
return;
|
|
@@ -1726,13 +1726,9 @@ function tile(pos=vec2(), size=tileSizeDefault, textureIndex=0)
|
|
|
1726
1726
|
if (typeof pos === 'number')
|
|
1727
1727
|
{
|
|
1728
1728
|
const textureInfo = textureInfos[textureIndex];
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
pos = vec2((pos%cols)*size.x, (pos/cols|0)*size.y);
|
|
1733
|
-
}
|
|
1734
|
-
else
|
|
1735
|
-
pos = vec2();
|
|
1729
|
+
ASSERT(textureInfo, 'Texture not loaded');
|
|
1730
|
+
const cols = textureInfo.size.x / size.x |0;
|
|
1731
|
+
pos = vec2((pos%cols)*size.x, (pos/cols|0)*size.y);
|
|
1736
1732
|
}
|
|
1737
1733
|
|
|
1738
1734
|
// return a tile info object
|
|
@@ -1759,13 +1755,23 @@ class TileInfo
|
|
|
1759
1755
|
this.textureIndex = textureIndex;
|
|
1760
1756
|
}
|
|
1761
1757
|
|
|
1762
|
-
/** Returns
|
|
1758
|
+
/** Returns a copy of this tile offset by a vector
|
|
1763
1759
|
* @param {Vector2} offset - Offset to apply in pixels
|
|
1764
1760
|
* @return {TileInfo}
|
|
1765
1761
|
*/
|
|
1766
1762
|
offset(offset)
|
|
1767
1763
|
{ return new TileInfo(this.pos.add(offset), this.size, this.textureIndex); }
|
|
1768
1764
|
|
|
1765
|
+
/** Returns a copy of this tile offset by a number of animation frames
|
|
1766
|
+
* @param {Number} frame - Offset to apply in animation frames
|
|
1767
|
+
* @return {TileInfo}
|
|
1768
|
+
*/
|
|
1769
|
+
frame(frame)
|
|
1770
|
+
{
|
|
1771
|
+
ASSERT(typeof frame == 'number');
|
|
1772
|
+
return this.offset(vec2(frame*this.size.x, 0));
|
|
1773
|
+
}
|
|
1774
|
+
|
|
1769
1775
|
/** Returns the texture info for this tile
|
|
1770
1776
|
* @return {TextureInfo}
|
|
1771
1777
|
*/
|
|
@@ -1821,6 +1827,11 @@ function worldToScreen(worldPos)
|
|
|
1821
1827
|
);
|
|
1822
1828
|
}
|
|
1823
1829
|
|
|
1830
|
+
/** Get the camera's visible area in world space
|
|
1831
|
+
* @return {Vector2}
|
|
1832
|
+
* @memberof Draw */
|
|
1833
|
+
function getCameraSize() { return mainCanvasSize.scale(1/cameraScale); }
|
|
1834
|
+
|
|
1824
1835
|
/** Draw textured tile centered in world space, with color applied if using WebGL
|
|
1825
1836
|
* @param {Vector2} pos - Center of the tile in world space
|
|
1826
1837
|
* @param {Vector2} [size=(1,1)] - Size of the tile in world space
|
|
@@ -1911,21 +1922,6 @@ function drawRect(pos, size, color, angle, useWebGL, screenSpace, context)
|
|
|
1911
1922
|
drawTile(pos, size, undefined, color, angle, false, undefined, useWebGL, screenSpace, context);
|
|
1912
1923
|
}
|
|
1913
1924
|
|
|
1914
|
-
/** Draw colored polygon using passed in points
|
|
1915
|
-
* @param {Array} points - Array of Vector2 points
|
|
1916
|
-
* @param {Color} [color=(1,1,1,1)]
|
|
1917
|
-
* @param {Boolean} [screenSpace=false]
|
|
1918
|
-
* @param {CanvasRenderingContext2D} [context=mainContext]
|
|
1919
|
-
* @memberof Draw */
|
|
1920
|
-
function drawPoly(points, color=new Color, screenSpace, context=mainContext)
|
|
1921
|
-
{
|
|
1922
|
-
context.fillStyle = color.toString();
|
|
1923
|
-
context.beginPath();
|
|
1924
|
-
for (const point of screenSpace ? points : points.map(worldToScreen))
|
|
1925
|
-
context.lineTo(point.x, point.y);
|
|
1926
|
-
context.fill();
|
|
1927
|
-
}
|
|
1928
|
-
|
|
1929
1925
|
/** Draw colored line between two points
|
|
1930
1926
|
* @param {Vector2} posA
|
|
1931
1927
|
* @param {Vector2} posB
|
|
@@ -2352,9 +2348,21 @@ function mouseToScreen(mousePos)
|
|
|
2352
2348
|
///////////////////////////////////////////////////////////////////////////////
|
|
2353
2349
|
// Gamepad input
|
|
2354
2350
|
|
|
2351
|
+
// gamepad internal variables
|
|
2355
2352
|
const stickData = [];
|
|
2353
|
+
|
|
2354
|
+
// gamepads are updated by engine every frame automatically
|
|
2356
2355
|
function gamepadsUpdate()
|
|
2357
2356
|
{
|
|
2357
|
+
const applyDeadZones = (v)=>
|
|
2358
|
+
{
|
|
2359
|
+
const min=.3, max=.8;
|
|
2360
|
+
const deadZone = (v)=>
|
|
2361
|
+
v > min ? percent( v, min, max) :
|
|
2362
|
+
v < -min ? -percent(-v, min, max) : 0;
|
|
2363
|
+
return vec2(deadZone(v.x), deadZone(-v.y)).clampLength();
|
|
2364
|
+
}
|
|
2365
|
+
|
|
2358
2366
|
// update touch gamepad if enabled
|
|
2359
2367
|
if (touchGamepadEnable && isTouchDevice)
|
|
2360
2368
|
{
|
|
@@ -2366,7 +2374,16 @@ function gamepadsUpdate()
|
|
|
2366
2374
|
{
|
|
2367
2375
|
// read virtual analog stick
|
|
2368
2376
|
const sticks = stickData[0] || (stickData[0] = []);
|
|
2369
|
-
sticks[0] = vec2(
|
|
2377
|
+
sticks[0] = vec2();
|
|
2378
|
+
if (touchGamepadAnalog)
|
|
2379
|
+
sticks[0] = applyDeadZones(touchGamepadStick);
|
|
2380
|
+
else if (touchGamepadStick.lengthSquared() > .3)
|
|
2381
|
+
{
|
|
2382
|
+
// convert to 8 way dpad
|
|
2383
|
+
sticks[0].x = Math.round(touchGamepadStick.x);
|
|
2384
|
+
sticks[0].y = -Math.round(touchGamepadStick.y);
|
|
2385
|
+
sticks[0] = sticks[0].clampLength();
|
|
2386
|
+
}
|
|
2370
2387
|
|
|
2371
2388
|
// read virtual gamepad buttons
|
|
2372
2389
|
const data = inputData[1] || (inputData[1] = []);
|
|
@@ -2378,7 +2395,12 @@ function gamepadsUpdate()
|
|
|
2378
2395
|
}
|
|
2379
2396
|
}
|
|
2380
2397
|
|
|
2381
|
-
|
|
2398
|
+
// return if gamepads are disabled or not supported
|
|
2399
|
+
if (!gamepadsEnable || !navigator || !navigator.getGamepads)
|
|
2400
|
+
return;
|
|
2401
|
+
|
|
2402
|
+
// only poll gamepads when focused or in debug mode
|
|
2403
|
+
if (!debug && !document.hasFocus())
|
|
2382
2404
|
return;
|
|
2383
2405
|
|
|
2384
2406
|
// poll gamepads
|
|
@@ -2392,14 +2414,9 @@ function gamepadsUpdate()
|
|
|
2392
2414
|
|
|
2393
2415
|
if (gamepad)
|
|
2394
2416
|
{
|
|
2395
|
-
// read clamp dead zone of analog sticks
|
|
2396
|
-
const deadZone = .3, deadZoneMax = .8, applyDeadZone = (v)=>
|
|
2397
|
-
v > deadZone ? percent( v, deadZone, deadZoneMax) :
|
|
2398
|
-
v < -deadZone ? -percent(-v, deadZone, deadZoneMax) : 0;
|
|
2399
|
-
|
|
2400
2417
|
// read analog sticks
|
|
2401
2418
|
for (let j = 0; j < gamepad.axes.length-1; j+=2)
|
|
2402
|
-
sticks[j>>1] = vec2(
|
|
2419
|
+
sticks[j>>1] = applyDeadZones(vec2(gamepad.axes[j],gamepad.axes[j+1]));
|
|
2403
2420
|
|
|
2404
2421
|
// read buttons
|
|
2405
2422
|
for (let j = gamepad.buttons.length; j--;)
|
|
@@ -2498,7 +2515,7 @@ function createTouchGamepad()
|
|
|
2498
2515
|
touchGamepadStick = vec2();
|
|
2499
2516
|
|
|
2500
2517
|
const touchHandler = ontouchstart;
|
|
2501
|
-
ontouchstart = ontouchmove = ontouchend = (e)=>
|
|
2518
|
+
ontouchstart = ontouchmove = ontouchend = (e)=>
|
|
2502
2519
|
{
|
|
2503
2520
|
// clear touch gamepad input
|
|
2504
2521
|
touchGamepadStick = vec2();
|
|
@@ -2528,14 +2545,7 @@ function createTouchGamepad()
|
|
|
2528
2545
|
if (touchPos.distance(stickCenter) < touchGamepadSize)
|
|
2529
2546
|
{
|
|
2530
2547
|
// virtual analog stick
|
|
2531
|
-
|
|
2532
|
-
touchGamepadStick = touchPos.subtract(stickCenter).scale(2/touchGamepadSize).clampLength();
|
|
2533
|
-
else
|
|
2534
|
-
{
|
|
2535
|
-
// 8 way dpad
|
|
2536
|
-
const angle = touchPos.subtract(stickCenter).angle();
|
|
2537
|
-
touchGamepadStick.setAngle((angle * 4 / PI + 8.5 | 0) * PI / 4);
|
|
2538
|
-
}
|
|
2548
|
+
touchGamepadStick = touchPos.subtract(stickCenter).scale(2/touchGamepadSize).clampLength();
|
|
2539
2549
|
}
|
|
2540
2550
|
else if (touchPos.distance(buttonCenter) < touchGamepadSize)
|
|
2541
2551
|
{
|
|
@@ -2628,7 +2638,7 @@ function touchGamepadRender()
|
|
|
2628
2638
|
|
|
2629
2639
|
|
|
2630
2640
|
/**
|
|
2631
|
-
* Sound Object - Stores a
|
|
2641
|
+
* Sound Object - Stores a sound for later use and can be played positionally
|
|
2632
2642
|
*
|
|
2633
2643
|
* <a href=https://killedbyapixel.github.io/ZzFX/>Create sounds using the ZzFX Sound Designer.</a>
|
|
2634
2644
|
* @example
|
|
@@ -2643,7 +2653,7 @@ class Sound
|
|
|
2643
2653
|
/** Create a sound object and cache the zzfx samples for later use
|
|
2644
2654
|
* @param {Array} zzfxSound - Array of zzfx parameters, ex. [.5,.5]
|
|
2645
2655
|
* @param {Number} [range=soundDefaultRange] - World space max range of sound, will not play if camera is farther away
|
|
2646
|
-
* @param {Number} [taper=soundDefaultTaper] - At what percentage of range should it start tapering
|
|
2656
|
+
* @param {Number} [taper=soundDefaultTaper] - At what percentage of range should it start tapering
|
|
2647
2657
|
*/
|
|
2648
2658
|
constructor(zzfxSound, range=soundDefaultRange, taper=soundDefaultTaper)
|
|
2649
2659
|
{
|
|
@@ -2794,7 +2804,7 @@ class SoundWave extends Sound
|
|
|
2794
2804
|
* 1, 0, 9, 1 // channel notes
|
|
2795
2805
|
* ],
|
|
2796
2806
|
* [ // channel 1
|
|
2797
|
-
* 0, 1, // instrument
|
|
2807
|
+
* 0, 1, // instrument 0, right speaker
|
|
2798
2808
|
* 0, 12, 17, -1 // channel notes
|
|
2799
2809
|
* ]
|
|
2800
2810
|
* ],
|
|
@@ -2823,24 +2833,24 @@ class Music extends Sound
|
|
|
2823
2833
|
|
|
2824
2834
|
/** Play the music
|
|
2825
2835
|
* @param {Number} [volume=1] - How much to scale volume by
|
|
2826
|
-
* @param {Boolean} [loop
|
|
2836
|
+
* @param {Boolean} [loop] - True if the music should loop
|
|
2827
2837
|
* @return {AudioBufferSourceNode} - The audio source node
|
|
2828
2838
|
*/
|
|
2829
|
-
playMusic(volume, loop
|
|
2839
|
+
playMusic(volume, loop=false)
|
|
2830
2840
|
{ return super.play(undefined, volume, 1, 1, loop); }
|
|
2831
2841
|
}
|
|
2832
2842
|
|
|
2833
2843
|
/** Play an mp3, ogg, or wav audio from a local file or url
|
|
2834
|
-
* @param {String}
|
|
2844
|
+
* @param {String} filename - Location of sound file to play
|
|
2835
2845
|
* @param {Number} [volume] - How much to scale volume by
|
|
2836
2846
|
* @param {Boolean} [loop] - True if the music should loop
|
|
2837
2847
|
* @return {HTMLAudioElement} - The audio element for this sound
|
|
2838
2848
|
* @memberof Audio */
|
|
2839
|
-
function playAudioFile(
|
|
2849
|
+
function playAudioFile(filename, volume=1, loop=false)
|
|
2840
2850
|
{
|
|
2841
2851
|
if (!soundEnable) return;
|
|
2842
2852
|
|
|
2843
|
-
const audio = new Audio(
|
|
2853
|
+
const audio = new Audio(filename);
|
|
2844
2854
|
audio.volume = soundVolume * volume;
|
|
2845
2855
|
audio.loop = loop;
|
|
2846
2856
|
audio.play();
|
|
@@ -2892,6 +2902,11 @@ function getNoteFrequency(semitoneOffset, rootFrequency=220)
|
|
|
2892
2902
|
* @memberof Audio */
|
|
2893
2903
|
let audioContext = new AudioContext;
|
|
2894
2904
|
|
|
2905
|
+
/** Keep track if audio was suspended when last sound was played
|
|
2906
|
+
* @type {Boolean}
|
|
2907
|
+
* @memberof Audio */
|
|
2908
|
+
let audioSuspended = false;
|
|
2909
|
+
|
|
2895
2910
|
/** Play cached audio samples with given settings
|
|
2896
2911
|
* @param {Array} sampleChannels - Array of arrays of samples to play (for stereo playback)
|
|
2897
2912
|
* @param {Number} [volume] - How much to scale volume by
|
|
@@ -2906,11 +2921,15 @@ function playSamples(sampleChannels, volume=1, rate=1, pan=0, loop=false, sample
|
|
|
2906
2921
|
if (!soundEnable) return;
|
|
2907
2922
|
|
|
2908
2923
|
// prevent sounds from building up if they can't be played
|
|
2909
|
-
|
|
2924
|
+
const audioWasSuspended = audioSuspended;
|
|
2925
|
+
if (audioSuspended = audioContext.state != 'running')
|
|
2910
2926
|
{
|
|
2911
2927
|
// fix stalled audio
|
|
2912
2928
|
audioContext.resume();
|
|
2913
|
-
|
|
2929
|
+
|
|
2930
|
+
// prevent suspended sounds from building up
|
|
2931
|
+
if (audioWasSuspended)
|
|
2932
|
+
return;
|
|
2914
2933
|
}
|
|
2915
2934
|
|
|
2916
2935
|
// create buffer and source
|
|
@@ -3178,7 +3197,7 @@ function zzfxM(instruments, patterns, sequence, BPM = 125)
|
|
|
3178
3197
|
/**
|
|
3179
3198
|
* LittleJS Tile Layer System
|
|
3180
3199
|
* - Caches arrays of tiles to off screen canvas for fast rendering
|
|
3181
|
-
* -
|
|
3200
|
+
* - Unlimited numbers of layers, allocates canvases as needed
|
|
3182
3201
|
* - Interfaces with EngineObject for collision
|
|
3183
3202
|
* - Collision layer is separate from visible layers
|
|
3184
3203
|
* - It is recommended to have a visible layer that matches the collision
|
|
@@ -3230,7 +3249,7 @@ function getTileCollisionData(pos)
|
|
|
3230
3249
|
|
|
3231
3250
|
/** Check if collision with another object should occur
|
|
3232
3251
|
* @param {Vector2} pos
|
|
3233
|
-
* @param {Vector2} [size=(
|
|
3252
|
+
* @param {Vector2} [size=(0,0)]
|
|
3234
3253
|
* @param {EngineObject} [object]
|
|
3235
3254
|
* @return {Boolean}
|
|
3236
3255
|
* @memberof TileCollision */
|
|
@@ -3747,16 +3766,17 @@ class ParticleEmitter extends EngineObject
|
|
|
3747
3766
|
|
|
3748
3767
|
// build particle
|
|
3749
3768
|
const particle = new Particle(pos, this.tileInfo, angle, colorStart, colorEnd, particleTime, sizeStart, sizeEnd, this.fadeRate, this.additive, this.trailScale, this.localSpace && this, this.particleDestroyCallback);
|
|
3750
|
-
particle.velocity
|
|
3751
|
-
particle.
|
|
3752
|
-
particle.
|
|
3753
|
-
particle.
|
|
3754
|
-
particle.
|
|
3755
|
-
particle.
|
|
3756
|
-
particle.
|
|
3757
|
-
particle.
|
|
3758
|
-
particle.
|
|
3759
|
-
particle.
|
|
3769
|
+
particle.velocity = vec2().setAngle(velocityAngle, speed);
|
|
3770
|
+
particle.angleVelocity = angleSpeed;
|
|
3771
|
+
particle.fadeRate = this.fadeRate;
|
|
3772
|
+
particle.damping = this.damping;
|
|
3773
|
+
particle.angleDamping = this.angleDamping;
|
|
3774
|
+
particle.elasticity = this.elasticity;
|
|
3775
|
+
particle.friction = this.friction;
|
|
3776
|
+
particle.gravityScale = this.gravityScale;
|
|
3777
|
+
particle.collideTiles = this.collideTiles;
|
|
3778
|
+
particle.renderOrder = this.renderOrder;
|
|
3779
|
+
particle.mirror = !!randInt(2);
|
|
3760
3780
|
|
|
3761
3781
|
// call particle create callaback
|
|
3762
3782
|
this.particleCreateCallback && this.particleCreateCallback(particle);
|
|
@@ -4147,7 +4167,7 @@ class Newgrounds
|
|
|
4147
4167
|
}
|
|
4148
4168
|
|
|
4149
4169
|
// build the input object
|
|
4150
|
-
const input =
|
|
4170
|
+
const input =
|
|
4151
4171
|
{
|
|
4152
4172
|
'app_id': this.app_id,
|
|
4153
4173
|
'session_id': this.session_id,
|
|
@@ -4362,8 +4382,6 @@ function glCreateTexture(image)
|
|
|
4362
4382
|
const filter = canvasPixelated ? gl_NEAREST : gl_LINEAR;
|
|
4363
4383
|
glContext.texParameteri(gl_TEXTURE_2D, gl_TEXTURE_MIN_FILTER, filter);
|
|
4364
4384
|
glContext.texParameteri(gl_TEXTURE_2D, gl_TEXTURE_MAG_FILTER, filter);
|
|
4365
|
-
glContext.texParameteri(gl_TEXTURE_2D, gl_TEXTURE_WRAP_S, gl_CLAMP_TO_EDGE);
|
|
4366
|
-
glContext.texParameteri(gl_TEXTURE_2D, gl_TEXTURE_WRAP_T, gl_CLAMP_TO_EDGE);
|
|
4367
4385
|
|
|
4368
4386
|
return texture;
|
|
4369
4387
|
}
|
|
@@ -4441,7 +4459,7 @@ function glDraw(x, y, sizeX, sizeY, angle, uv0X, uv0Y, uv1X, uv1Y, rgba, rgbaAdd
|
|
|
4441
4459
|
///////////////////////////////////////////////////////////////////////////////
|
|
4442
4460
|
// post processing - can be enabled to pass other canvases through a final shader
|
|
4443
4461
|
|
|
4444
|
-
let glPostShader,
|
|
4462
|
+
let glPostShader, glPostTexture, glPostIncludeOverlay;
|
|
4445
4463
|
|
|
4446
4464
|
/** Set up a post processing shader
|
|
4447
4465
|
* @param {String} shaderCode
|
|
@@ -4477,7 +4495,6 @@ function glInitPostProcess(shaderCode, includeOverlay=false)
|
|
|
4477
4495
|
);
|
|
4478
4496
|
|
|
4479
4497
|
// create buffer and texture
|
|
4480
|
-
glPostArrayBuffer = glContext.createBuffer();
|
|
4481
4498
|
glPostTexture = glCreateTexture(undefined);
|
|
4482
4499
|
glPostIncludeOverlay = includeOverlay;
|
|
4483
4500
|
|
|
@@ -4549,10 +4566,7 @@ gl_NEAREST = 9728,
|
|
|
4549
4566
|
gl_LINEAR = 9729,
|
|
4550
4567
|
gl_TEXTURE_MAG_FILTER = 10240,
|
|
4551
4568
|
gl_TEXTURE_MIN_FILTER = 10241,
|
|
4552
|
-
gl_TEXTURE_WRAP_S = 10242,
|
|
4553
|
-
gl_TEXTURE_WRAP_T = 10243,
|
|
4554
4569
|
gl_COLOR_BUFFER_BIT = 16384,
|
|
4555
|
-
gl_CLAMP_TO_EDGE = 33071,
|
|
4556
4570
|
gl_TEXTURE0 = 33984,
|
|
4557
4571
|
gl_ARRAY_BUFFER = 34962,
|
|
4558
4572
|
gl_STATIC_DRAW = 35044,
|
|
@@ -4569,7 +4583,7 @@ gl_MAX_INSTANCES = 1e4,
|
|
|
4569
4583
|
gl_INSTANCE_BYTE_STRIDE = gl_INDICIES_PER_INSTANCE * 4, // 11 * 4
|
|
4570
4584
|
gl_INSTANCE_BUFFER_SIZE = gl_MAX_INSTANCES * gl_INSTANCE_BYTE_STRIDE;
|
|
4571
4585
|
/**
|
|
4572
|
-
* LittleJS - The Tiny JavaScript Game Engine
|
|
4586
|
+
* LittleJS - The Tiny Fast JavaScript Game Engine
|
|
4573
4587
|
* MIT License - Copyright 2021 Frank Force
|
|
4574
4588
|
*
|
|
4575
4589
|
* Engine Features
|
|
@@ -4600,9 +4614,9 @@ const engineName = 'LittleJS';
|
|
|
4600
4614
|
* @type {String}
|
|
4601
4615
|
* @default
|
|
4602
4616
|
* @memberof Engine */
|
|
4603
|
-
const engineVersion = '1.9.
|
|
4617
|
+
const engineVersion = '1.9.4';
|
|
4604
4618
|
|
|
4605
|
-
/** Frames per second to update
|
|
4619
|
+
/** Frames per second to update
|
|
4606
4620
|
* @type {Number}
|
|
4607
4621
|
* @default
|
|
4608
4622
|
* @memberof Engine */
|
|
@@ -4619,7 +4633,7 @@ const timeDelta = 1/frameRate;
|
|
|
4619
4633
|
* @memberof Engine */
|
|
4620
4634
|
let engineObjects = [];
|
|
4621
4635
|
|
|
4622
|
-
/** Array
|
|
4636
|
+
/** Array with only objects set to collide with other objects this frame (for optimization)
|
|
4623
4637
|
* @type {Array}
|
|
4624
4638
|
* @memberof Engine */
|
|
4625
4639
|
let engineObjectsCollide = [];
|
|
@@ -4629,7 +4643,7 @@ let engineObjectsCollide = [];
|
|
|
4629
4643
|
* @memberof Engine */
|
|
4630
4644
|
let frame = 0;
|
|
4631
4645
|
|
|
4632
|
-
/** Current engine time since start in seconds
|
|
4646
|
+
/** Current engine time since start in seconds
|
|
4633
4647
|
* @type {Number}
|
|
4634
4648
|
* @memberof Engine */
|
|
4635
4649
|
let time = 0;
|
|
@@ -4655,12 +4669,12 @@ let frameTimeLastMS = 0, frameTimeBufferMS = 0, averageFPS = 0;
|
|
|
4655
4669
|
|
|
4656
4670
|
///////////////////////////////////////////////////////////////////////////////
|
|
4657
4671
|
|
|
4658
|
-
/**
|
|
4659
|
-
* @param {Function} gameInit
|
|
4660
|
-
* @param {Function} gameUpdate
|
|
4661
|
-
* @param {Function} gameUpdatePost
|
|
4662
|
-
* @param {Function} gameRender
|
|
4663
|
-
* @param {Function} gameRenderPost
|
|
4672
|
+
/** Startup LittleJS engine with your callback functions
|
|
4673
|
+
* @param {Function} gameInit - Called once after the engine starts up, setup the game
|
|
4674
|
+
* @param {Function} gameUpdate - Called every frame at 60 frames per second, handle input and update the game state
|
|
4675
|
+
* @param {Function} gameUpdatePost - Called after physics and objects are updated, setup camera and prepare for render
|
|
4676
|
+
* @param {Function} gameRender - Called before objects are rendered, draw any background effects that appear behind objects
|
|
4677
|
+
* @param {Function} gameRenderPost - Called after objects are rendered, draw effects or hud that appear above all objects
|
|
4664
4678
|
* @param {Array} [imageSources=['tiles.png']] - Image to load
|
|
4665
4679
|
* @memberof Engine */
|
|
4666
4680
|
function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRenderPost, imageSources=['tiles.png'])
|
|
@@ -4682,33 +4696,8 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
4682
4696
|
timeReal += frameTimeDeltaMS / 1e3;
|
|
4683
4697
|
frameTimeBufferMS += paused ? 0 : frameTimeDeltaMS;
|
|
4684
4698
|
if (!debugSpeedUp)
|
|
4685
|
-
frameTimeBufferMS = min(frameTimeBufferMS, 50); // clamp
|
|
4686
|
-
|
|
4687
|
-
if (canvasFixedSize.x)
|
|
4688
|
-
{
|
|
4689
|
-
// clear canvas and set fixed size
|
|
4690
|
-
mainCanvas.width = canvasFixedSize.x;
|
|
4691
|
-
mainCanvas.height = canvasFixedSize.y;
|
|
4692
|
-
|
|
4693
|
-
// fit to window by adding space on top or bottom if necessary
|
|
4694
|
-
const aspect = innerWidth / innerHeight;
|
|
4695
|
-
const fixedAspect = mainCanvas.width / mainCanvas.height;
|
|
4696
|
-
(glCanvas||mainCanvas).style.width = mainCanvas.style.width = overlayCanvas.style.width = aspect < fixedAspect ? '100%' : '';
|
|
4697
|
-
(glCanvas||mainCanvas).style.height = mainCanvas.style.height = overlayCanvas.style.height = aspect < fixedAspect ? '' : '100%';
|
|
4698
|
-
}
|
|
4699
|
-
else
|
|
4700
|
-
{
|
|
4701
|
-
// clear canvas and set size to same as window
|
|
4702
|
-
mainCanvas.width = min(innerWidth, canvasMaxSize.x);
|
|
4703
|
-
mainCanvas.height = min(innerHeight, canvasMaxSize.y);
|
|
4704
|
-
}
|
|
4705
|
-
|
|
4706
|
-
// clear overlay canvas and set size
|
|
4707
|
-
overlayCanvas.width = mainCanvas.width;
|
|
4708
|
-
overlayCanvas.height = mainCanvas.height;
|
|
4709
|
-
|
|
4710
|
-
// save canvas size
|
|
4711
|
-
mainCanvasSize = vec2(mainCanvas.width, mainCanvas.height);
|
|
4699
|
+
frameTimeBufferMS = min(frameTimeBufferMS, 50); // clamp in case of slow framerate
|
|
4700
|
+
updateCanvas();
|
|
4712
4701
|
|
|
4713
4702
|
if (paused)
|
|
4714
4703
|
{
|
|
@@ -4782,6 +4771,35 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
4782
4771
|
requestAnimationFrame(engineUpdate);
|
|
4783
4772
|
}
|
|
4784
4773
|
|
|
4774
|
+
function updateCanvas()
|
|
4775
|
+
{
|
|
4776
|
+
if (canvasFixedSize.x)
|
|
4777
|
+
{
|
|
4778
|
+
// clear canvas and set fixed size
|
|
4779
|
+
mainCanvas.width = canvasFixedSize.x;
|
|
4780
|
+
mainCanvas.height = canvasFixedSize.y;
|
|
4781
|
+
|
|
4782
|
+
// fit to window by adding space on top or bottom if necessary
|
|
4783
|
+
const aspect = innerWidth / innerHeight;
|
|
4784
|
+
const fixedAspect = mainCanvas.width / mainCanvas.height;
|
|
4785
|
+
(glCanvas||mainCanvas).style.width = mainCanvas.style.width = overlayCanvas.style.width = aspect < fixedAspect ? '100%' : '';
|
|
4786
|
+
(glCanvas||mainCanvas).style.height = mainCanvas.style.height = overlayCanvas.style.height = aspect < fixedAspect ? '' : '100%';
|
|
4787
|
+
}
|
|
4788
|
+
else
|
|
4789
|
+
{
|
|
4790
|
+
// clear canvas and set size to same as window
|
|
4791
|
+
mainCanvas.width = min(innerWidth, canvasMaxSize.x);
|
|
4792
|
+
mainCanvas.height = min(innerHeight, canvasMaxSize.y);
|
|
4793
|
+
}
|
|
4794
|
+
|
|
4795
|
+
// clear overlay canvas and set size
|
|
4796
|
+
overlayCanvas.width = mainCanvas.width;
|
|
4797
|
+
overlayCanvas.height = mainCanvas.height;
|
|
4798
|
+
|
|
4799
|
+
// save canvas size
|
|
4800
|
+
mainCanvasSize = vec2(mainCanvas.width, mainCanvas.height);
|
|
4801
|
+
}
|
|
4802
|
+
|
|
4785
4803
|
// setup html
|
|
4786
4804
|
const styleBody =
|
|
4787
4805
|
'margin:0;overflow:hidden;' + // fill the window
|
|
@@ -4806,6 +4824,7 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
4806
4824
|
const styleCanvas = 'position:absolute;' + // position
|
|
4807
4825
|
'top:50%;left:50%;transform:translate(-50%,-50%)'; // center
|
|
4808
4826
|
(glCanvas||mainCanvas).style.cssText = mainCanvas.style.cssText = overlayCanvas.style.cssText = styleCanvas;
|
|
4827
|
+
updateCanvas();
|
|
4809
4828
|
|
|
4810
4829
|
// create promises for loading images
|
|
4811
4830
|
const promises = imageSources.map((src, textureIndex)=>
|
|
@@ -5002,9 +5021,9 @@ function drawEngineSplashScreen(t)
|
|
|
5002
5021
|
|
|
5003
5022
|
// big stack
|
|
5004
5023
|
rect(50,20,10,-10,color(0,1));
|
|
5005
|
-
rect(50,20,6,-10,color(0,2));
|
|
5006
|
-
rect(50,20,3,-10,color(0,3));
|
|
5007
|
-
rect(50,
|
|
5024
|
+
rect(50,20,6.5,-10,color(0,2));
|
|
5025
|
+
rect(50,20,3.5,-10,color(0,3));
|
|
5026
|
+
rect(50,20,10,-10);
|
|
5008
5027
|
circle(55,2,11.4,.5,PI-.5,color(3,3));
|
|
5009
5028
|
circle(55,2,11.4,.5,PI/2,color(3,2),1);
|
|
5010
5029
|
circle(55,2,11.4,.5,PI-.5);
|
|
@@ -5023,7 +5042,7 @@ function drawEngineSplashScreen(t)
|
|
|
5023
5042
|
|
|
5024
5043
|
// engine outline
|
|
5025
5044
|
circle(36,30,10,PI/2,PI*3/2);
|
|
5026
|
-
circle(
|
|
5045
|
+
circle(48,30,10,PI/2,PI*3/2);
|
|
5027
5046
|
circle(60,30,10);
|
|
5028
5047
|
line(36,20,60,20);
|
|
5029
5048
|
|
package/examples/logo.png
CHANGED
|
Binary file
|
package/examples/module/game.js
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
// import module
|
|
10
10
|
import * as LittleJS from '../../dist/littlejs.esm.js';
|
|
11
|
-
const {
|
|
11
|
+
const {tile, vec2, hsl} = LittleJS;
|
|
12
12
|
|
|
13
13
|
// show the LittleJS splash screen
|
|
14
14
|
LittleJS.setShowSplashScreen(true);
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
2
|
<map version="1.10" tiledversion="1.11.0" orientation="orthogonal" renderorder="right-down" width="256" height="64" tilewidth="16" tileheight="16" infinite="0" nextlayerid="3" nextobjectid="1">
|
|
3
3
|
<editorsettings>
|
|
4
|
-
<export target="../
|
|
4
|
+
<export target="../gameLevelData.js" format="js"/>
|
|
5
5
|
</editorsettings>
|
|
6
|
-
<tileset firstgid="1" source="
|
|
6
|
+
<tileset firstgid="1" source="gameLevelData.tsx"/>
|
|
7
7
|
<layer id="2" name="Tile Layer 2" width="256" height="64" tintcolor="#4d7caf">
|
|
8
8
|
<data encoding="csv">
|
|
9
9
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
|
@@ -103,7 +103,7 @@
|
|
|
103
103
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,0,0,0,2,5,2,5,4,0,0,0,0,0,0,0,0,0,0,0,0,0,5,5,0,0,0,0,0,0,5,5,0,0,0,0,0,0,0,0,0,5,4,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,5,2,0,0,0,0,0,0,0,0,0,0,0,4,0,0,2,0,0,0,0,0,2,2,0,0,0,0,5,0,0,5,0,0,5,0,0,0,0,0,2,0,0,0,4,0,0,0,0,0,0,2,2,0,0,0,0,0,0,5,0,0,5,0,0,0,0,0,0,0,0,0,0,5,5,0,0,5,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,5,2,0,0,0,0,0,0,0,0,0,0,0,5,5,5,5,5,5,0,0,0,0,0,0,0,0,4,0,0,0,3,3,3,3,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
|
104
104
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,5,5,0,0,0,0,0,0,0,0,5,5,0,0,0,0,0,0,5,5,0,0,0,0,0,0,0,0,0,5,4,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,5,5,0,0,5,5,0,0,0,0,0,0,0,0,5,5,5,0,0,5,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,5,5,5,5,5,5,0,0,0,0,0,0,0,0,4,0,0,0,20,20,20,20,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
|
105
105
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,5,5,0,0,0,0,0,0,18,0,5,5,0,0,0,0,0,0,5,5,0,0,18,0,0,0,0,0,0,5,4,5,0,0,0,0,0,0,0,0,0,0,5,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,5,5,5,0,0,5,5,5,0,0,0,0,0,0,5,5,5,5,0,0,5,5,5,0,0,0,0,0,0,18,18,0,0,0,0,0,0,0,0,0,0,0,0,0,18,18,0,5,5,5,5,5,5,5,5,0,0,0,0,0,0,0,0,4,0,0,0,20,20,20,20,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
|
106
|
-
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,17,0,0,0,0,0,0,0,0,0,19,0,4,0,0,0,5,5,0,0,0,0,0,18,18,0,5,5,0,0,19,0,0,0,5,5,0,18,18,18,0,19,
|
|
106
|
+
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,17,0,0,0,0,0,0,0,0,0,19,0,4,0,0,0,5,5,0,0,0,0,0,18,18,0,5,5,0,0,19,0,0,0,5,5,0,18,18,18,0,19,0,0,0,5,4,5,0,0,0,0,0,0,0,0,0,0,5,5,0,0,0,0,0,0,0,0,0,0,0,0,18,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,19,0,0,0,0,0,0,0,0,0,0,0,0,19,0,0,0,0,0,4,0,19,0,0,0,0,0,0,0,0,0,5,5,5,5,0,0,5,5,5,5,0,19,0,0,5,5,5,5,5,0,0,5,5,5,5,0,0,0,0,0,18,18,0,0,0,0,0,19,0,0,0,19,0,0,0,18,18,5,5,5,5,5,5,5,5,5,0,0,0,0,0,0,0,0,5,0,0,0,20,20,20,20,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
|
107
107
|
2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,2,2,3,2,2,2,3,2,2,2,2,2,3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,2,2,2,2,3,2,2,2,2,2,2,2,2,4,2,2,2,2,3,2,2,2,2,2,2,5,5,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
|
|
108
108
|
2,2,2,2,2,2,2,2,2,2,2,2,3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,2,2,2,2,2,2,3,2,2,2,2,2,2,2,2,2,2,3,2,2,3,2,3,2,2,2,4,2,2,2,2,2,2,2,2,3,2,2,5,5,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,2,2,2,3,2,2,2,2,2,2,2,2,2,2,3,2,2,2,2,2,2,3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,2,2,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,2,2,2,2,2,2,2,3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,2,2,2,2,2,2,2,2,2,3,2,2,2,2,2,2,2,2,2,2,2,2,2,3,2,2,2,2,2,2,2,2,
|
|
109
109
|
2,2,3,2,2,3,2,2,3,2,2,2,2,2,2,3,2,2,2,2,2,2,2,2,2,2,2,3,2,2,2,2,2,3,2,2,2,2,2,2,2,2,2,2,2,3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,4,0,0,0,0,0,0,0,0,0,0,0,5,5,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,2,2,3,2,2,2,2,3,2,2,2,2,2,2,2,2,3,2,2,2,2,2,2,2,2,2,2,2,3,2,2,2,2,2,2,2,2,2,2,2,2,3,2,2,2,2,2,2,2,3,2,2,2,2,2,2,3,2,2,2,2,2,2,2,0,0,2,2,3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
|