littlejsengine 1.10.4 → 1.10.7
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 +8 -78
- package/dist/littlejs.d.ts +31 -17
- package/dist/littlejs.esm.js +121 -55
- package/dist/littlejs.esm.min.js +1 -1
- package/dist/littlejs.js +121 -55
- package/dist/littlejs.min.js +1 -1
- package/dist/littlejs.release.js +121 -55
- package/examples/box2d/game.js +15 -4
- package/examples/box2d/gameObjects.js +10 -9
- package/examples/box2d/index.html +5 -5
- package/examples/box2d/scenes.js +4 -4
- package/examples/box2d/tiles.png +0 -0
- package/examples/breakout/index.html +3 -3
- package/examples/breakoutTutorial/index.html +2 -2
- package/examples/electron/game.js +3 -0
- package/examples/electron/index.html +2 -2
- package/examples/htmlMenu/index.html +3 -3
- package/examples/js13k/game.js +3 -0
- package/examples/js13k/index.html +13 -13
- package/examples/module/game.js +3 -0
- package/examples/module/index.html +1 -1
- package/examples/particles/index.html +4 -1
- package/examples/platformer/game.js +19 -15
- package/examples/platformer/gameCharacter.js +3 -3
- package/examples/platformer/gameLevel.js +1 -0
- package/examples/platformer/index.html +8 -8
- package/examples/platformer/tiles.png +0 -0
- package/examples/puzzle/index.html +2 -2
- package/examples/starter/game.js +3 -0
- package/examples/starter/index.html +13 -13
- package/examples/stress/index.html +1 -1
- package/examples/typescript/game.js +2 -0
- package/examples/typescript/game.ts +3 -0
- package/examples/typescript/index.html +1 -1
- package/examples/uiSystem/game.js +23 -16
- package/examples/uiSystem/index.html +3 -14
- package/package.json +1 -1
- package/plugins/postProcess.js +2 -9
- package/plugins/uiSystem.js +84 -24
- package/src/engine.js +11 -11
- package/src/engineAudio.js +7 -13
- package/src/engineDraw.js +1 -1
- package/src/engineInput.js +7 -3
- package/src/engineMedals.js +19 -5
- package/src/engineObject.js +8 -5
- package/src/engineTileLayer.js +8 -7
- package/src/engineUtilities.js +59 -10
- package/examples/js13k/build/index.html +0 -1
- package/examples/js13k/build/index.js +0 -1
- package/examples/js13k/build/tiles.png +0 -0
- package/examples/js13k/game - Copy.zip +0 -0
- package/examples/js13k/game.zip +0 -0
- package/examples/starter/build/index.html +0 -2
- package/examples/starter/build/index.js +0 -1
- package/examples/starter/build/tiles.png +0 -0
- package/examples/starter/game.zip +0 -0
- package/examples/typescript/build/build/littlejs.esm.js +0 -4327
- package/examples/typescript/build/dist/littlejs.esm.js +0 -4425
- package/examples/typescript/build/examples/typescript/build.js +0 -24
- package/examples/typescript/build/examples/typescript/game.js +0 -100
- package/examples/typescript/build/examples/typescript/test/build/littlejs.esm.js +0 -3934
- package/examples/typescript/build/examples/typescript/test/examples/typescript/build.js +0 -86
- package/examples/typescript/build/examples/typescript/test/examples/typescript/game.js +0 -92
package/dist/littlejs.js
CHANGED
|
@@ -671,7 +671,8 @@ function formatTime(t) { return (t/60|0) + ':' + (t%60<10?'0':'') + (t%60|0); }
|
|
|
671
671
|
* @memberof Random */
|
|
672
672
|
function rand(valueA=1, valueB=0) { return valueB + Math.random() * (valueA-valueB); }
|
|
673
673
|
|
|
674
|
-
/** Returns a floored random value the two values passed in
|
|
674
|
+
/** Returns a floored random value between the two values passed in
|
|
675
|
+
* The upper bound is exclusive. (If 2 is passed in, result will be 0 or 1)
|
|
675
676
|
* @param {Number} valueA
|
|
676
677
|
* @param {Number} [valueB]
|
|
677
678
|
* @return {Number}
|
|
@@ -800,18 +801,24 @@ class Vector2
|
|
|
800
801
|
* @param {Number} [y] - Y axis location */
|
|
801
802
|
constructor(x=0, y=0)
|
|
802
803
|
{
|
|
803
|
-
ASSERT(typeof x == 'number' && typeof y == 'number');
|
|
804
804
|
/** @property {Number} - X axis location */
|
|
805
805
|
this.x = x;
|
|
806
806
|
/** @property {Number} - Y axis location */
|
|
807
807
|
this.y = y;
|
|
808
|
+
ASSERT(this.isValid());
|
|
808
809
|
}
|
|
809
810
|
|
|
810
811
|
/** Sets values of this vector and returns self
|
|
811
812
|
* @param {Number} [x] - X axis location
|
|
812
813
|
* @param {Number} [y] - Y axis location
|
|
813
814
|
* @return {Vector2} */
|
|
814
|
-
set(x=0, y=0)
|
|
815
|
+
set(x=0, y=0)
|
|
816
|
+
{
|
|
817
|
+
this.x = x;
|
|
818
|
+
this.y = y;
|
|
819
|
+
ASSERT(this.isValid());
|
|
820
|
+
return this;
|
|
821
|
+
}
|
|
815
822
|
|
|
816
823
|
/** Returns a new vector that is a copy of this
|
|
817
824
|
* @return {Vector2} */
|
|
@@ -1003,6 +1010,14 @@ class Vector2
|
|
|
1003
1010
|
if (debug)
|
|
1004
1011
|
return `(${(this.x<0?'':' ') + this.x.toFixed(digits)},${(this.y<0?'':' ') + this.y.toFixed(digits)} )`;
|
|
1005
1012
|
}
|
|
1013
|
+
|
|
1014
|
+
/** Checks if this is a valid vector
|
|
1015
|
+
* @return {Boolean} */
|
|
1016
|
+
isValid()
|
|
1017
|
+
{
|
|
1018
|
+
return typeof this.x == 'number' && !isNaN(this.x)
|
|
1019
|
+
&& typeof this.y == 'number' && !isNaN(this.y);
|
|
1020
|
+
}
|
|
1006
1021
|
}
|
|
1007
1022
|
|
|
1008
1023
|
///////////////////////////////////////////////////////////////////////////////
|
|
@@ -1063,6 +1078,7 @@ class Color
|
|
|
1063
1078
|
this.b = b;
|
|
1064
1079
|
/** @property {Number} - Alpha */
|
|
1065
1080
|
this.a = a;
|
|
1081
|
+
ASSERT(this.isValid());
|
|
1066
1082
|
}
|
|
1067
1083
|
|
|
1068
1084
|
/** Sets values of this color and returns self
|
|
@@ -1072,7 +1088,14 @@ class Color
|
|
|
1072
1088
|
* @param {Number} [a] - alpha
|
|
1073
1089
|
* @return {Color} */
|
|
1074
1090
|
set(r=1, g=1, b=1, a=1)
|
|
1075
|
-
{
|
|
1091
|
+
{
|
|
1092
|
+
this.r = r;
|
|
1093
|
+
this.g = g;
|
|
1094
|
+
this.b = b;
|
|
1095
|
+
this.a = a;
|
|
1096
|
+
ASSERT(this.isValid());
|
|
1097
|
+
return this;
|
|
1098
|
+
}
|
|
1076
1099
|
|
|
1077
1100
|
/** Returns a new color that is a copy of this
|
|
1078
1101
|
* @return {Color} */
|
|
@@ -1155,6 +1178,7 @@ class Color
|
|
|
1155
1178
|
this.g = f(p, q, h);
|
|
1156
1179
|
this.b = f(p, q, h - 1/3);
|
|
1157
1180
|
this.a = a;
|
|
1181
|
+
ASSERT(this.isValid());
|
|
1158
1182
|
return this;
|
|
1159
1183
|
}
|
|
1160
1184
|
|
|
@@ -1182,7 +1206,6 @@ class Color
|
|
|
1182
1206
|
else if (b == max)
|
|
1183
1207
|
h = (r - g) / d + 4;
|
|
1184
1208
|
}
|
|
1185
|
-
|
|
1186
1209
|
return [h / 6, s, l, a];
|
|
1187
1210
|
}
|
|
1188
1211
|
|
|
@@ -1215,11 +1238,27 @@ class Color
|
|
|
1215
1238
|
* @return {Color} */
|
|
1216
1239
|
setHex(hex)
|
|
1217
1240
|
{
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1241
|
+
ASSERT(typeof hex == 'string' && hex[0] == '#');
|
|
1242
|
+
ASSERT([4,5,7,9].includes(hex.length), 'Invalid hex');
|
|
1243
|
+
|
|
1244
|
+
if (hex.length < 6)
|
|
1245
|
+
{
|
|
1246
|
+
const fromHex = (c)=> clamp(parseInt(hex[c],16)/15);
|
|
1247
|
+
this.r = fromHex(1);
|
|
1248
|
+
this.g = fromHex(2),
|
|
1249
|
+
this.b = fromHex(3);
|
|
1250
|
+
this.a = hex.length == 5 ? fromHex(4) : 1;
|
|
1251
|
+
}
|
|
1252
|
+
else
|
|
1253
|
+
{
|
|
1254
|
+
const fromHex = (c)=> clamp(parseInt(hex.slice(c,c+2),16)/255);
|
|
1255
|
+
this.r = fromHex(1);
|
|
1256
|
+
this.g = fromHex(3),
|
|
1257
|
+
this.b = fromHex(5);
|
|
1258
|
+
this.a = hex.length == 9 ? fromHex(7) : 1;
|
|
1259
|
+
}
|
|
1260
|
+
|
|
1261
|
+
ASSERT(this.isValid());
|
|
1223
1262
|
return this;
|
|
1224
1263
|
}
|
|
1225
1264
|
|
|
@@ -1233,6 +1272,16 @@ class Color
|
|
|
1233
1272
|
const a = clamp(this.a)*255<<24;
|
|
1234
1273
|
return r + g + b + a;
|
|
1235
1274
|
}
|
|
1275
|
+
|
|
1276
|
+
/** Checks if this is a valid color
|
|
1277
|
+
* @return {Boolean} */
|
|
1278
|
+
isValid()
|
|
1279
|
+
{
|
|
1280
|
+
return typeof this.r == 'number' && !isNaN(this.r)
|
|
1281
|
+
&& typeof this.g == 'number' && !isNaN(this.g)
|
|
1282
|
+
&& typeof this.b == 'number' && !isNaN(this.b)
|
|
1283
|
+
&& typeof this.a == 'number' && !isNaN(this.a);
|
|
1284
|
+
}
|
|
1236
1285
|
}
|
|
1237
1286
|
|
|
1238
1287
|
///////////////////////////////////////////////////////////////////////////////
|
|
@@ -1868,7 +1917,7 @@ class EngineObject
|
|
|
1868
1917
|
* @param {Color} [color=(1,1,1,1)] - Color to apply to tile when rendered
|
|
1869
1918
|
* @param {Number} [renderOrder] - Objects sorted by renderOrder before being rendered
|
|
1870
1919
|
*/
|
|
1871
|
-
constructor(pos=vec2(), size=vec2(1), tileInfo, angle=0, color, renderOrder=0)
|
|
1920
|
+
constructor(pos=vec2(), size=vec2(1), tileInfo, angle=0, color=new Color, renderOrder=0)
|
|
1872
1921
|
{
|
|
1873
1922
|
// set passed in params
|
|
1874
1923
|
ASSERT(isVector2(pos) && isVector2(size), 'ensure pos and size are vec2s');
|
|
@@ -1982,15 +2031,18 @@ class EngineObject
|
|
|
1982
2031
|
|
|
1983
2032
|
// apply physics
|
|
1984
2033
|
const oldPos = this.pos.copy();
|
|
1985
|
-
this.
|
|
1986
|
-
this.
|
|
1987
|
-
|
|
2034
|
+
this.velocity.x *= this.damping;
|
|
2035
|
+
this.velocity.y *= this.damping;
|
|
2036
|
+
if (this.mass) // dont apply gravity to static objects
|
|
2037
|
+
this.velocity.y += gravity * this.gravityScale;
|
|
2038
|
+
this.pos.x += this.velocity.x;
|
|
2039
|
+
this.pos.y += this.velocity.y;
|
|
1988
2040
|
this.angle += this.angleVelocity *= this.angleDamping;
|
|
1989
2041
|
|
|
1990
2042
|
// physics sanity checks
|
|
1991
2043
|
ASSERT(this.angleDamping >= 0 && this.angleDamping <= 1);
|
|
1992
2044
|
ASSERT(this.damping >= 0 && this.damping <= 1);
|
|
1993
|
-
if (!enablePhysicsSolver || !this.mass) // dont do collision for
|
|
2045
|
+
if (!enablePhysicsSolver || !this.mass) // dont do collision for static objects
|
|
1994
2046
|
return;
|
|
1995
2047
|
|
|
1996
2048
|
const wasMovingDown = this.velocity.y < 0;
|
|
@@ -2351,7 +2403,7 @@ let drawCount;
|
|
|
2351
2403
|
* tile(2) // a tile at index 2 using the default tile size of 16
|
|
2352
2404
|
* tile(5, 8) // a tile at index 5 using a tile size of 8
|
|
2353
2405
|
* tile(1, 16, 3) // a tile at index 1 of size 16 on texture 3
|
|
2354
|
-
* tile(vec2(4,8), vec2(30,10)) // a tile at
|
|
2406
|
+
* tile(vec2(4,8), vec2(30,10)) // a tile at index (4,8) with a size of (30,10)
|
|
2355
2407
|
* @memberof Draw
|
|
2356
2408
|
*/
|
|
2357
2409
|
function tile(pos=vec2(), size=tileSizeDefault, textureIndex=0, padding=0)
|
|
@@ -2900,7 +2952,7 @@ function keyWasReleased(key, device=0)
|
|
|
2900
2952
|
|
|
2901
2953
|
/** Clears all input
|
|
2902
2954
|
* @memberof Input */
|
|
2903
|
-
function clearInput() { inputData = [[]]; }
|
|
2955
|
+
function clearInput() { inputData = [[]]; touchGamepadButtons = []; }
|
|
2904
2956
|
|
|
2905
2957
|
/** Returns true if mouse button is down
|
|
2906
2958
|
* @function
|
|
@@ -3065,6 +3117,7 @@ function inputInit()
|
|
|
3065
3117
|
onmousemove = (e)=> mousePosScreen = mouseToScreen(e);
|
|
3066
3118
|
onwheel = (e)=> mouseWheel = e.ctrlKey ? 0 : sign(e.deltaY);
|
|
3067
3119
|
oncontextmenu = (e)=> false; // prevent right click menu
|
|
3120
|
+
onblur = (e) => clearInput(); // reset input when focus is lost
|
|
3068
3121
|
|
|
3069
3122
|
// init touch input
|
|
3070
3123
|
if (isTouchDevice && touchInputEnable)
|
|
@@ -3263,10 +3316,13 @@ function touchInputInit()
|
|
|
3263
3316
|
if (touching)
|
|
3264
3317
|
{
|
|
3265
3318
|
touchGamepadTimer.set();
|
|
3266
|
-
if (paused)
|
|
3319
|
+
if (paused && !wasTouching)
|
|
3267
3320
|
{
|
|
3268
3321
|
// touch anywhere to press start when paused
|
|
3269
3322
|
touchGamepadButtons[9] = 1;
|
|
3323
|
+
|
|
3324
|
+
// call default touch handler so normal touch events still work
|
|
3325
|
+
handleTouchDefault(e);
|
|
3270
3326
|
return;
|
|
3271
3327
|
}
|
|
3272
3328
|
}
|
|
@@ -3291,7 +3347,7 @@ function touchInputInit()
|
|
|
3291
3347
|
const button = touchPos.subtract(buttonCenter).direction();
|
|
3292
3348
|
touchGamepadButtons[button] = 1;
|
|
3293
3349
|
}
|
|
3294
|
-
else if (touchPos.distance(startCenter) < touchGamepadSize)
|
|
3350
|
+
else if (touchPos.distance(startCenter) < touchGamepadSize && !wasTouching)
|
|
3295
3351
|
{
|
|
3296
3352
|
// virtual start button in center
|
|
3297
3353
|
touchGamepadButtons[9] = 1;
|
|
@@ -3379,7 +3435,7 @@ function touchGamepadRender()
|
|
|
3379
3435
|
/** Audio context used by the engine
|
|
3380
3436
|
* @type {AudioContext}
|
|
3381
3437
|
* @memberof Audio */
|
|
3382
|
-
let audioContext;
|
|
3438
|
+
let audioContext = new AudioContext;
|
|
3383
3439
|
|
|
3384
3440
|
/** Master gain node for all audio to pass through
|
|
3385
3441
|
* @type {GainNode}
|
|
@@ -3390,14 +3446,10 @@ function audioInit()
|
|
|
3390
3446
|
{
|
|
3391
3447
|
if (!soundEnable || headlessMode) return;
|
|
3392
3448
|
|
|
3393
|
-
// create audio context
|
|
3394
|
-
audioContext = new AudioContext;
|
|
3395
|
-
|
|
3396
|
-
// create and connect gain node
|
|
3397
3449
|
// (createGain is more widely spported then GainNode construtor)
|
|
3398
3450
|
audioGainNode = audioContext.createGain();
|
|
3399
3451
|
audioGainNode.connect(audioContext.destination);
|
|
3400
|
-
|
|
3452
|
+
audioGainNode.gain.value = soundVolume; // set starting value
|
|
3401
3453
|
}
|
|
3402
3454
|
|
|
3403
3455
|
///////////////////////////////////////////////////////////////////////////////
|
|
@@ -3432,12 +3484,15 @@ class Sound
|
|
|
3432
3484
|
|
|
3433
3485
|
/** @property {Number} - How much to randomize frequency each time sound plays */
|
|
3434
3486
|
this.randomness = 0;
|
|
3487
|
+
|
|
3488
|
+
/** @property {GainNode} - Gain node for this sound */
|
|
3489
|
+
this.gainNode = audioContext.createGain();
|
|
3435
3490
|
|
|
3436
3491
|
if (zzfxSound)
|
|
3437
3492
|
{
|
|
3438
3493
|
// generate zzfx sound now for fast playback
|
|
3439
3494
|
const defaultRandomness = .05;
|
|
3440
|
-
this.randomness = zzfxSound[1]
|
|
3495
|
+
this.randomness = zzfxSound[1] != undefined ? zzfxSound[1] : defaultRandomness;
|
|
3441
3496
|
zzfxSound[1] = 0; // generate without randomness
|
|
3442
3497
|
this.sampleChannels = [zzfxG(...zzfxSound)];
|
|
3443
3498
|
this.sampleRate = zzfxR;
|
|
@@ -3478,18 +3533,13 @@ class Sound
|
|
|
3478
3533
|
|
|
3479
3534
|
// play the sound
|
|
3480
3535
|
const playbackRate = pitch + pitch * this.randomness*randomnessScale*rand(-1,1);
|
|
3481
|
-
this.gainNode = audioContext.createGain();
|
|
3482
3536
|
return this.source = playSamples(this.sampleChannels, volume, playbackRate, pan, loop, this.sampleRate, this.gainNode);
|
|
3483
3537
|
}
|
|
3484
3538
|
|
|
3485
3539
|
/** Set the sound volume
|
|
3486
3540
|
* @param {Number} [volume] - How much to scale volume by
|
|
3487
3541
|
*/
|
|
3488
|
-
setVolume(volume=1)
|
|
3489
|
-
{
|
|
3490
|
-
if (this.gainNode)
|
|
3491
|
-
this.gainNode.gain.value = volume;
|
|
3492
|
-
}
|
|
3542
|
+
setVolume(volume=1) { this.gainNode.gain.value = volume; }
|
|
3493
3543
|
|
|
3494
3544
|
/** Stop the last instance of this sound that was played */
|
|
3495
3545
|
stop()
|
|
@@ -3973,18 +4023,18 @@ function zzfxM(instruments, patterns, sequence, BPM = 125)
|
|
|
3973
4023
|
|
|
3974
4024
|
|
|
3975
4025
|
|
|
3976
|
-
/** The tile collision layer
|
|
4026
|
+
/** The tile collision layer grid, use setTileCollisionData and getTileCollisionData to access
|
|
3977
4027
|
* @type {Array}
|
|
3978
4028
|
* @memberof TileCollision */
|
|
3979
4029
|
let tileCollision = [];
|
|
3980
4030
|
|
|
3981
|
-
/** Size of the tile collision layer
|
|
4031
|
+
/** Size of the tile collision layer 2d grid
|
|
3982
4032
|
* @type {Vector2}
|
|
3983
4033
|
* @memberof TileCollision */
|
|
3984
4034
|
let tileCollisionSize = vec2();
|
|
3985
4035
|
|
|
3986
4036
|
/** Clear and initialize tile collision
|
|
3987
|
-
* @param {Vector2} size
|
|
4037
|
+
* @param {Vector2} size - width and height of tile collision 2d grid
|
|
3988
4038
|
* @memberof TileCollision */
|
|
3989
4039
|
function initTileCollision(size)
|
|
3990
4040
|
{
|
|
@@ -3994,7 +4044,7 @@ function initTileCollision(size)
|
|
|
3994
4044
|
tileCollision[i] = 0;
|
|
3995
4045
|
}
|
|
3996
4046
|
|
|
3997
|
-
/** Set tile collision data
|
|
4047
|
+
/** Set tile collision data for a given cell in the grid
|
|
3998
4048
|
* @param {Vector2} pos
|
|
3999
4049
|
* @param {Number} [data]
|
|
4000
4050
|
* @memberof TileCollision */
|
|
@@ -4003,7 +4053,7 @@ function setTileCollisionData(pos, data=0)
|
|
|
4003
4053
|
pos.arrayCheck(tileCollisionSize) && (tileCollision[(pos.y|0)*tileCollisionSize.x+pos.x|0] = data);
|
|
4004
4054
|
}
|
|
4005
4055
|
|
|
4006
|
-
/** Get tile collision data
|
|
4056
|
+
/** Get tile collision data for a given cell in the grid
|
|
4007
4057
|
* @param {Vector2} pos
|
|
4008
4058
|
* @return {Number}
|
|
4009
4059
|
* @memberof TileCollision */
|
|
@@ -4034,7 +4084,8 @@ function tileCollisionTest(pos, size=vec2(), object)
|
|
|
4034
4084
|
return false;
|
|
4035
4085
|
}
|
|
4036
4086
|
|
|
4037
|
-
/** Return the center of first tile hit
|
|
4087
|
+
/** Return the center of first tile hit, undefined if nothing was hit.
|
|
4088
|
+
* This does not return the exact intersection, but the center of the tile hit.
|
|
4038
4089
|
* @param {Vector2} posStart
|
|
4039
4090
|
* @param {Vector2} posEnd
|
|
4040
4091
|
* @param {EngineObject} [object]
|
|
@@ -4279,8 +4330,8 @@ class TileLayer extends EngineObject
|
|
|
4279
4330
|
const d = this.getData(layerPos);
|
|
4280
4331
|
if (d.tile != undefined)
|
|
4281
4332
|
{
|
|
4282
|
-
const pos = this.pos.add(layerPos).add(vec2(.5));
|
|
4283
4333
|
ASSERT(mainContext == this.context, 'must call redrawStart() before drawing tiles');
|
|
4334
|
+
const pos = layerPos.add(vec2(.5));
|
|
4284
4335
|
const tileInfo = tile(d.tile, s, this.tileInfo.textureIndex);
|
|
4285
4336
|
drawTile(pos, vec2(1), tileInfo, d.color, d.direction*PI/2, d.mirror);
|
|
4286
4337
|
}
|
|
@@ -4709,7 +4760,7 @@ function medalsInit(saveName)
|
|
|
4709
4760
|
// check if medals are unlocked
|
|
4710
4761
|
medalsSaveName = saveName;
|
|
4711
4762
|
if (!debugMedals)
|
|
4712
|
-
medalsForEach(medal=> medal.unlocked =
|
|
4763
|
+
medalsForEach(medal=> medal.unlocked = !!localStorage[medal.storageKey()]);
|
|
4713
4764
|
|
|
4714
4765
|
// engine automatically renders medals
|
|
4715
4766
|
engineAddPlugin(undefined, medalsRender);
|
|
@@ -4772,14 +4823,28 @@ class Medal
|
|
|
4772
4823
|
constructor(id, name, description='', icon='🏆', src)
|
|
4773
4824
|
{
|
|
4774
4825
|
ASSERT(id >= 0 && !medals[id]);
|
|
4775
|
-
|
|
4776
|
-
|
|
4777
|
-
|
|
4826
|
+
|
|
4827
|
+
/** @property {Number} - The unique identifier of the medal */
|
|
4828
|
+
this.id = id;
|
|
4829
|
+
|
|
4830
|
+
/** @property {String} - Name of the medal */
|
|
4778
4831
|
this.name = name;
|
|
4832
|
+
|
|
4833
|
+
/** @property {String} - Description of the medal */
|
|
4779
4834
|
this.description = description;
|
|
4835
|
+
|
|
4836
|
+
/** @property {String} - Icon for the medal */
|
|
4780
4837
|
this.icon = icon;
|
|
4838
|
+
|
|
4839
|
+
/** @property {Boolean} - Is the medal unlocked? */
|
|
4840
|
+
this.unlocked = false;
|
|
4841
|
+
|
|
4842
|
+
// load the source image if provided
|
|
4781
4843
|
if (src)
|
|
4782
4844
|
(this.image = new Image).src = src;
|
|
4845
|
+
|
|
4846
|
+
// add this to list of medals
|
|
4847
|
+
medals[id] = this;
|
|
4783
4848
|
}
|
|
4784
4849
|
|
|
4785
4850
|
/** Unlocks a medal if not already unlocked */
|
|
@@ -4790,7 +4855,7 @@ class Medal
|
|
|
4790
4855
|
|
|
4791
4856
|
// save the medal
|
|
4792
4857
|
ASSERT(medalsSaveName, 'save name must be set');
|
|
4793
|
-
localStorage[this.storageKey()] = this.unlocked =
|
|
4858
|
+
localStorage[this.storageKey()] = this.unlocked = true;
|
|
4794
4859
|
medalsDisplayQueue.push(this);
|
|
4795
4860
|
}
|
|
4796
4861
|
|
|
@@ -5198,7 +5263,7 @@ const engineName = 'LittleJS';
|
|
|
5198
5263
|
* @type {String}
|
|
5199
5264
|
* @default
|
|
5200
5265
|
* @memberof Engine */
|
|
5201
|
-
const engineVersion = '1.10.
|
|
5266
|
+
const engineVersion = '1.10.7';
|
|
5202
5267
|
|
|
5203
5268
|
/** Frames per second to update
|
|
5204
5269
|
* @type {Number}
|
|
@@ -5261,6 +5326,8 @@ const pluginUpdateList = [], pluginRenderList = [];
|
|
|
5261
5326
|
* @memberof Engine */
|
|
5262
5327
|
function engineAddPlugin(updateFunction, renderFunction)
|
|
5263
5328
|
{
|
|
5329
|
+
ASSERT(!pluginUpdateList.includes(updateFunction));
|
|
5330
|
+
ASSERT(!pluginRenderList.includes(renderFunction));
|
|
5264
5331
|
updateFunction && pluginUpdateList.push(updateFunction);
|
|
5265
5332
|
renderFunction && pluginRenderList.push(renderFunction);
|
|
5266
5333
|
}
|
|
@@ -5269,12 +5336,12 @@ function engineAddPlugin(updateFunction, renderFunction)
|
|
|
5269
5336
|
// Main engine functions
|
|
5270
5337
|
|
|
5271
5338
|
/** Startup LittleJS engine with your callback functions
|
|
5272
|
-
* @param {Function} gameInit
|
|
5273
|
-
* @param {Function} gameUpdate
|
|
5274
|
-
* @param {Function} gameUpdatePost - Called after physics and objects are updated,
|
|
5275
|
-
* @param {Function} gameRender
|
|
5276
|
-
* @param {Function} gameRenderPost - Called after objects are rendered,
|
|
5277
|
-
* @param {Array} [imageSources=[]] -
|
|
5339
|
+
* @param {Function|function():Promise} gameInit - Called once after the engine starts up
|
|
5340
|
+
* @param {Function} gameUpdate - Called every frame before objects are updated
|
|
5341
|
+
* @param {Function} gameUpdatePost - Called after physics and objects are updated, even when paused
|
|
5342
|
+
* @param {Function} gameRender - Called before objects are rendered, for drawing the background
|
|
5343
|
+
* @param {Function} gameRenderPost - Called after objects are rendered, useful for drawing UI
|
|
5344
|
+
* @param {Array} [imageSources=[]] - List of images to load
|
|
5278
5345
|
* @param {HTMLElement} [rootElement] - Root element to attach to, the document body by default
|
|
5279
5346
|
* @memberof Engine */
|
|
5280
5347
|
function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRenderPost, imageSources=[], rootElement=document.body)
|
|
@@ -5424,8 +5491,7 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
5424
5491
|
|
|
5425
5492
|
function startEngine()
|
|
5426
5493
|
{
|
|
5427
|
-
gameInit();
|
|
5428
|
-
engineUpdate();
|
|
5494
|
+
new Promise((resolve) => resolve(gameInit())).then(engineUpdate);
|
|
5429
5495
|
}
|
|
5430
5496
|
|
|
5431
5497
|
if (headlessMode)
|
|
@@ -5440,7 +5506,6 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
5440
5506
|
'width:100vw;height:100vh;' + // fill the window
|
|
5441
5507
|
'display:flex;' + // use flexbox
|
|
5442
5508
|
'align-items:center;' + // horizontal center
|
|
5443
|
-
(canvasPixelated ? 'image-rendering:pixelated;' : '') + // pixel art
|
|
5444
5509
|
'justify-content:center;' + // vertical center
|
|
5445
5510
|
'background:#000;' + // set background color
|
|
5446
5511
|
'user-select:none;' + // prevent hold to select
|
|
@@ -5778,4 +5843,5 @@ function drawEngineSplashScreen(t)
|
|
|
5778
5843
|
}
|
|
5779
5844
|
|
|
5780
5845
|
x.restore();
|
|
5781
|
-
}
|
|
5846
|
+
}
|
|
5847
|
+
|