littlejsengine 1.13.1 → 1.14.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/README.md +15 -11
- package/copyToGithub.bat +28 -0
- package/dist/littlejs.d.ts +430 -304
- package/dist/littlejs.esm.js +1745 -982
- package/dist/littlejs.esm.min.js +1 -1
- package/dist/littlejs.js +1716 -965
- package/dist/littlejs.min.js +1 -1
- package/dist/littlejs.release.js +1673 -922
- package/examples/box2d/game.js +2 -2
- package/examples/box2d/gameObjects.js +2 -2
- package/examples/box2d/scenes.js +1 -1
- package/examples/breakout/game.js +30 -25
- package/examples/breakoutTutorial/README.md +44 -41
- package/examples/breakoutTutorial/game.js +2 -2
- package/examples/electron/build.bat +7 -0
- package/examples/electron/build.js +124 -0
- package/examples/electron/electron.js +35 -0
- package/examples/electron/game.js +140 -0
- package/examples/electron/index.html +13 -0
- package/examples/electron/package.json +12 -0
- package/examples/electron/tiles.png +0 -0
- package/examples/empty/game.js +1 -0
- package/examples/htmlMenu/game.js +1 -1
- package/examples/index.html +332 -160
- package/examples/module/game.js +5 -2
- package/examples/particles/index.html +12 -3
- package/examples/platformer/gameEffects.js +20 -19
- package/examples/platformer/gameLevel.js +6 -1
- package/examples/shorts/base.html +1 -1
- package/examples/shorts/blending.js +9 -5
- package/examples/shorts/box2d.js +1 -1
- package/examples/shorts/box2dCar.js +1 -1
- package/examples/shorts/clock.js +1 -1
- package/examples/shorts/colors.js +2 -2
- package/examples/shorts/flappyGame.js +2 -1
- package/examples/shorts/helloWorld.js +1 -1
- package/examples/shorts/maze.js +1 -1
- package/examples/shorts/music.js +106 -0
- package/examples/shorts/nineSlice.js +9 -9
- package/examples/shorts/parallax.js +71 -0
- package/examples/shorts/piano.js +9 -10
- package/examples/shorts/postProcess.js +25 -8
- package/examples/shorts/raycasting.js +2 -2
- package/examples/shorts/shapes.js +7 -15
- package/examples/shorts/slidingPuzzle.js +2 -2
- package/examples/shorts/song.mp3 +0 -0
- package/examples/shorts/starfield.js +5 -7
- package/examples/shorts/systemFont.js +1 -1
- package/examples/shorts/uiSystem.js +16 -8
- package/examples/starter/build.js +9 -8
- package/examples/starter/game.js +5 -2
- package/examples/starter/index.html +2 -2
- package/examples/stress/index.html +13 -8
- package/examples/style.css +19 -6
- package/examples/typescript/build.js +1 -1
- package/examples/typescript/game.js +2 -2
- package/examples/typescript/game.ts +5 -2
- package/examples/uiSystem/game.js +13 -12
- package/package.json +2 -2
- package/plugins/box2d.js +24 -97
- package/plugins/drawUtilities.js +29 -23
- package/plugins/newgrounds.js +6 -6
- package/plugins/pluginExport.js +1 -1
- package/plugins/postProcess.js +7 -0
- package/plugins/uiSystem.js +106 -82
- package/plugins/zzfxm.js +10 -10
- package/reference.md +90 -54
- package/src/engine.js +28 -23
- package/src/engineAudio.js +285 -110
- package/src/engineBuild.js +9 -9
- package/src/engineDebug.js +28 -28
- package/src/engineDraw.js +346 -202
- package/src/engineExport.js +28 -16
- package/src/engineInput.js +58 -68
- package/src/engineMedals.js +29 -29
- package/src/engineObject.js +28 -25
- package/src/engineParticles.js +67 -60
- package/src/engineRelease.js +1 -1
- package/src/engineSettings.js +70 -16
- package/src/engineTileLayer.js +34 -34
- package/src/engineUtilities.js +69 -62
- package/src/engineWebGL.js +467 -65
- /package/examples/shorts/{playSound.js → sound.js} +0 -0
package/src/engineExport.js
CHANGED
|
@@ -52,9 +52,11 @@ export
|
|
|
52
52
|
cameraPos,
|
|
53
53
|
cameraAngle,
|
|
54
54
|
cameraScale,
|
|
55
|
+
canvasColorTiles,
|
|
55
56
|
canvasMaxSize,
|
|
56
57
|
canvasFixedSize,
|
|
57
58
|
canvasPixelated,
|
|
59
|
+
overlayCanvasPixelated,
|
|
58
60
|
tilesPixelated,
|
|
59
61
|
fontDefault,
|
|
60
62
|
showSplashScreen,
|
|
@@ -91,9 +93,11 @@ export
|
|
|
91
93
|
setCameraPos,
|
|
92
94
|
setCameraAngle,
|
|
93
95
|
setCameraScale,
|
|
96
|
+
setCanvasColorTiles,
|
|
94
97
|
setCanvasMaxSize,
|
|
95
98
|
setCanvasFixedSize,
|
|
96
99
|
setCanvasPixelated,
|
|
100
|
+
setOverlayCanvasPixelated,
|
|
97
101
|
setTilesPixelated,
|
|
98
102
|
setFontDefault,
|
|
99
103
|
setShowSplashScreen,
|
|
@@ -169,6 +173,8 @@ export
|
|
|
169
173
|
rgb,
|
|
170
174
|
hsl,
|
|
171
175
|
isColor,
|
|
176
|
+
isVector2,
|
|
177
|
+
isNumber,
|
|
172
178
|
|
|
173
179
|
// Default Colors
|
|
174
180
|
WHITE,
|
|
@@ -219,27 +225,32 @@ export
|
|
|
219
225
|
// WebGL
|
|
220
226
|
glCanvas,
|
|
221
227
|
glContext,
|
|
228
|
+
glClearCanvas,
|
|
229
|
+
glSetTexture,
|
|
222
230
|
glCompileShader,
|
|
223
|
-
glCopyToContext,
|
|
224
231
|
glCreateProgram,
|
|
225
232
|
glCreateTexture,
|
|
226
233
|
glDeleteTexture,
|
|
227
234
|
glSetTextureData,
|
|
228
|
-
glDraw,
|
|
229
235
|
glFlush,
|
|
230
|
-
|
|
236
|
+
glCopyToContext,
|
|
231
237
|
glSetAntialias,
|
|
232
|
-
|
|
238
|
+
glDraw,
|
|
239
|
+
glDrawPointsTransform,
|
|
240
|
+
glDrawOutlineTransform,
|
|
241
|
+
glDrawPoints,
|
|
233
242
|
glAntialias,
|
|
234
|
-
glShader,
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
glPositionData,
|
|
239
|
-
glColorData,
|
|
240
|
-
glInstanceCount,
|
|
241
|
-
glAdditive,
|
|
243
|
+
glShader,
|
|
244
|
+
glPolyShader,
|
|
245
|
+
glPolyMode,
|
|
246
|
+
glAdditive,
|
|
242
247
|
glBatchAdditive,
|
|
248
|
+
glActiveTexture,
|
|
249
|
+
glArrayBuffer,
|
|
250
|
+
glGeometryBuffer,
|
|
251
|
+
glPositionData,
|
|
252
|
+
glColorData,
|
|
253
|
+
glBatchCount,
|
|
243
254
|
|
|
244
255
|
// Input
|
|
245
256
|
keyIsDown,
|
|
@@ -272,17 +283,18 @@ export
|
|
|
272
283
|
pointerLockIsActive,
|
|
273
284
|
|
|
274
285
|
// Audio
|
|
286
|
+
audioContext,
|
|
287
|
+
audioMasterGain,
|
|
288
|
+
audioDefaultSampleRate,
|
|
275
289
|
Sound,
|
|
276
290
|
SoundWave,
|
|
277
|
-
|
|
291
|
+
SoundInstance,
|
|
278
292
|
speak,
|
|
279
293
|
speakStop,
|
|
280
294
|
getNoteFrequency,
|
|
281
295
|
playSamples,
|
|
282
296
|
zzfx,
|
|
283
297
|
zzfxG,
|
|
284
|
-
zzfxR,
|
|
285
|
-
audioContext,
|
|
286
298
|
|
|
287
299
|
// Base Object
|
|
288
300
|
EngineObject,
|
|
@@ -307,4 +319,4 @@ export
|
|
|
307
319
|
medalsPreventUnlock,
|
|
308
320
|
medalsInit,
|
|
309
321
|
Medal,
|
|
310
|
-
}
|
|
322
|
+
}
|
package/src/engineInput.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/**
|
|
1
|
+
/**
|
|
2
2
|
* LittleJS Input System
|
|
3
3
|
* - Tracks keyboard down, pressed, and released
|
|
4
4
|
* - Tracks mouse buttons, position, and wheel
|
|
@@ -16,10 +16,10 @@
|
|
|
16
16
|
* @return {boolean}
|
|
17
17
|
* @memberof Input */
|
|
18
18
|
function keyIsDown(key, device=0)
|
|
19
|
-
{
|
|
19
|
+
{
|
|
20
20
|
ASSERT(key !== undefined, 'key is undefined');
|
|
21
|
-
ASSERT(device > 0 || typeof key
|
|
22
|
-
return inputData[device] && !!(inputData[device][key] & 1);
|
|
21
|
+
ASSERT(device > 0 || typeof key !== 'number' || key < 3, 'use code string for keyboard');
|
|
22
|
+
return inputData[device] && !!(inputData[device][key] & 1);
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
/** Returns true if device key was pressed this frame
|
|
@@ -28,10 +28,10 @@ function keyIsDown(key, device=0)
|
|
|
28
28
|
* @return {boolean}
|
|
29
29
|
* @memberof Input */
|
|
30
30
|
function keyWasPressed(key, device=0)
|
|
31
|
-
{
|
|
31
|
+
{
|
|
32
32
|
ASSERT(key !== undefined, 'key is undefined');
|
|
33
|
-
ASSERT(device > 0 || typeof key
|
|
34
|
-
return inputData[device] && !!(inputData[device][key] & 2);
|
|
33
|
+
ASSERT(device > 0 || typeof key !== 'number' || key < 3, 'use code string for keyboard');
|
|
34
|
+
return inputData[device] && !!(inputData[device][key] & 2);
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
/** Returns true if device key was released this frame
|
|
@@ -40,9 +40,9 @@ function keyWasPressed(key, device=0)
|
|
|
40
40
|
* @return {boolean}
|
|
41
41
|
* @memberof Input */
|
|
42
42
|
function keyWasReleased(key, device=0)
|
|
43
|
-
{
|
|
43
|
+
{
|
|
44
44
|
ASSERT(key !== undefined, 'key is undefined');
|
|
45
|
-
ASSERT(device > 0 || typeof key
|
|
45
|
+
ASSERT(device > 0 || typeof key !== 'number' || key < 3, 'use code string for keyboard');
|
|
46
46
|
return inputData[device] && !!(inputData[device][key] & 4);
|
|
47
47
|
}
|
|
48
48
|
|
|
@@ -164,7 +164,7 @@ function gamepadWasReleased(button, gamepad=0)
|
|
|
164
164
|
* @param {number} [gamepad]
|
|
165
165
|
* @return {Vector2}
|
|
166
166
|
* @memberof Input */
|
|
167
|
-
function gamepadStick(stick,
|
|
167
|
+
function gamepadStick(stick, gamepad=0)
|
|
168
168
|
{ return gamepadStickData[gamepad] ? gamepadStickData[gamepad][stick] || vec2() : vec2(); }
|
|
169
169
|
|
|
170
170
|
///////////////////////////////////////////////////////////////////////////////
|
|
@@ -241,10 +241,10 @@ function inputInit()
|
|
|
241
241
|
{
|
|
242
242
|
// handle remapping wasd keys to directions
|
|
243
243
|
return inputWASDEmulateDirection ?
|
|
244
|
-
c
|
|
245
|
-
c
|
|
246
|
-
c
|
|
247
|
-
c
|
|
244
|
+
c === 'KeyW' ? 'ArrowUp' :
|
|
245
|
+
c === 'KeyS' ? 'ArrowDown' :
|
|
246
|
+
c === 'KeyA' ? 'ArrowLeft' :
|
|
247
|
+
c === 'KeyD' ? 'ArrowRight' : c : c;
|
|
248
248
|
}
|
|
249
249
|
function onMouseDown(e)
|
|
250
250
|
{
|
|
@@ -252,9 +252,9 @@ function inputInit()
|
|
|
252
252
|
return;
|
|
253
253
|
|
|
254
254
|
// fix stalled audio requiring user interaction
|
|
255
|
-
if (soundEnable && !headlessMode && audioContext &&
|
|
255
|
+
if (soundEnable && !headlessMode && audioContext && !audioIsRunning())
|
|
256
256
|
audioContext.resume();
|
|
257
|
-
|
|
257
|
+
|
|
258
258
|
isUsingGamepad = false;
|
|
259
259
|
inputData[0][e.button] = 3;
|
|
260
260
|
mousePosScreen = mouseEventToScreen(vec2(e.x,e.y));
|
|
@@ -297,8 +297,8 @@ function gamepadsUpdate()
|
|
|
297
297
|
const applyDeadZones = (v)=>
|
|
298
298
|
{
|
|
299
299
|
const min=.3, max=.8;
|
|
300
|
-
const deadZone = (v)=>
|
|
301
|
-
v >
|
|
300
|
+
const deadZone = (v)=>
|
|
301
|
+
v > min ? percent(v, min, max) :
|
|
302
302
|
v < -min ? -percent(-v, min, max) : 0;
|
|
303
303
|
return vec2(deadZone(v.x), deadZone(-v.y)).clampLength();
|
|
304
304
|
}
|
|
@@ -306,30 +306,29 @@ function gamepadsUpdate()
|
|
|
306
306
|
// update touch gamepad if enabled
|
|
307
307
|
if (touchGamepadEnable && isTouchDevice)
|
|
308
308
|
{
|
|
309
|
-
|
|
310
|
-
|
|
309
|
+
if (!touchGamepadTimer.isSet())
|
|
310
|
+
return;
|
|
311
|
+
|
|
312
|
+
// read virtual analog stick
|
|
313
|
+
const sticks = gamepadStickData[0] || (gamepadStickData[0] = []);
|
|
314
|
+
sticks[0] = vec2();
|
|
315
|
+
if (touchGamepadAnalog)
|
|
316
|
+
sticks[0] = applyDeadZones(touchGamepadStick);
|
|
317
|
+
else if (touchGamepadStick.lengthSquared() > .3)
|
|
311
318
|
{
|
|
312
|
-
//
|
|
313
|
-
|
|
314
|
-
sticks[0] =
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
else if (touchGamepadStick.lengthSquared() > .3)
|
|
318
|
-
{
|
|
319
|
-
// convert to 8 way dpad
|
|
320
|
-
sticks[0].x = Math.round(touchGamepadStick.x);
|
|
321
|
-
sticks[0].y = -Math.round(touchGamepadStick.y);
|
|
322
|
-
sticks[0] = sticks[0].clampLength();
|
|
323
|
-
}
|
|
319
|
+
// convert to 8 way dpad
|
|
320
|
+
sticks[0].x = Math.round(touchGamepadStick.x);
|
|
321
|
+
sticks[0].y = -Math.round(touchGamepadStick.y);
|
|
322
|
+
sticks[0] = sticks[0].clampLength();
|
|
323
|
+
}
|
|
324
324
|
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
}
|
|
325
|
+
// read virtual gamepad buttons
|
|
326
|
+
const data = inputData[1] || (inputData[1] = []);
|
|
327
|
+
for (let i=10; i--;)
|
|
328
|
+
{
|
|
329
|
+
const j = i === 3 ? 2 : i === 2 ? 3 : i; // fix button locations
|
|
330
|
+
const wasDown = gamepadIsDown(j,0);
|
|
331
|
+
data[j] = touchGamepadButtons[i] ? wasDown ? 1 : 3 : wasDown ? 4 : 0;
|
|
333
332
|
}
|
|
334
333
|
}
|
|
335
334
|
|
|
@@ -355,7 +354,7 @@ function gamepadsUpdate()
|
|
|
355
354
|
// read analog sticks
|
|
356
355
|
for (let j = 0; j < gamepad.axes.length-1; j+=2)
|
|
357
356
|
sticks[j>>1] = applyDeadZones(vec2(gamepad.axes[j],gamepad.axes[j+1]));
|
|
358
|
-
|
|
357
|
+
|
|
359
358
|
// read buttons
|
|
360
359
|
for (let j = gamepad.buttons.length; j--;)
|
|
361
360
|
{
|
|
@@ -371,14 +370,14 @@ function gamepadsUpdate()
|
|
|
371
370
|
{
|
|
372
371
|
// copy dpad to left analog stick when pressed
|
|
373
372
|
const dpad = vec2(
|
|
374
|
-
(gamepadIsDown(15,i)&&1) - (gamepadIsDown(14,i)&&1),
|
|
373
|
+
(gamepadIsDown(15,i)&&1) - (gamepadIsDown(14,i)&&1),
|
|
375
374
|
(gamepadIsDown(12,i)&&1) - (gamepadIsDown(13,i)&&1));
|
|
376
375
|
if (dpad.lengthSquared())
|
|
377
376
|
sticks[0] = dpad.clampLength();
|
|
378
377
|
}
|
|
379
378
|
|
|
380
379
|
// disable touch gamepad if using real gamepad
|
|
381
|
-
touchGamepadEnable && isUsingGamepad && touchGamepadTimer.unset();
|
|
380
|
+
touchGamepadEnable && isUsingGamepad && touchGamepadTimer.unset();
|
|
382
381
|
}
|
|
383
382
|
}
|
|
384
383
|
}
|
|
@@ -403,20 +402,13 @@ function vibrateStop() { vibrate(0); }
|
|
|
403
402
|
const isTouchDevice = !headlessMode && window.ontouchstart !== undefined;
|
|
404
403
|
|
|
405
404
|
// touch gamepad internal variables
|
|
406
|
-
let touchGamepadTimer = new Timer, touchGamepadButtons, touchGamepadStick;
|
|
405
|
+
let touchGamepadTimer = new Timer, touchGamepadButtons = [], touchGamepadStick = vec2();
|
|
407
406
|
|
|
408
407
|
// enable touch input mouse passthrough
|
|
409
408
|
function touchInputInit()
|
|
410
409
|
{
|
|
411
410
|
// add non passive touch event listeners
|
|
412
411
|
let handleTouch = handleTouchDefault;
|
|
413
|
-
if (touchGamepadEnable)
|
|
414
|
-
{
|
|
415
|
-
// touch input internal variables
|
|
416
|
-
handleTouch = handleTouchGamepad;
|
|
417
|
-
touchGamepadButtons = [];
|
|
418
|
-
touchGamepadStick = vec2();
|
|
419
|
-
}
|
|
420
412
|
document.addEventListener('touchstart', (e) => handleTouch(e), { passive: false });
|
|
421
413
|
document.addEventListener('touchmove', (e) => handleTouch(e), { passive: false });
|
|
422
414
|
document.addEventListener('touchend', (e) => handleTouch(e), { passive: false });
|
|
@@ -425,8 +417,15 @@ function touchInputInit()
|
|
|
425
417
|
let wasTouching;
|
|
426
418
|
function handleTouchDefault(e)
|
|
427
419
|
{
|
|
420
|
+
if (!touchInputEnable)
|
|
421
|
+
return;
|
|
422
|
+
|
|
423
|
+
// route touch to gamepad
|
|
424
|
+
if (touchGamepadEnable)
|
|
425
|
+
handleTouchGamepad(e);
|
|
426
|
+
|
|
428
427
|
// fix stalled audio requiring user interaction
|
|
429
|
-
if (soundEnable && !headlessMode && audioContext &&
|
|
428
|
+
if (soundEnable && !headlessMode && audioContext && !audioIsRunning())
|
|
430
429
|
audioContext.resume();
|
|
431
430
|
|
|
432
431
|
// check if touching and pass to mouse events
|
|
@@ -455,7 +454,7 @@ function touchInputInit()
|
|
|
455
454
|
// prevent default handling like copy and magnifier lens
|
|
456
455
|
if (inputPreventDefault && document.hasFocus()) // allow document to get focus
|
|
457
456
|
e.preventDefault();
|
|
458
|
-
|
|
457
|
+
|
|
459
458
|
// must return true so the document will get focus
|
|
460
459
|
return true;
|
|
461
460
|
}
|
|
@@ -467,7 +466,7 @@ function touchInputInit()
|
|
|
467
466
|
touchGamepadStick = vec2();
|
|
468
467
|
touchGamepadButtons = [];
|
|
469
468
|
isUsingGamepad = true;
|
|
470
|
-
|
|
469
|
+
|
|
471
470
|
const touching = e.touches.length;
|
|
472
471
|
if (touching)
|
|
473
472
|
{
|
|
@@ -476,9 +475,6 @@ function touchInputInit()
|
|
|
476
475
|
{
|
|
477
476
|
// touch anywhere to press start when paused
|
|
478
477
|
touchGamepadButtons[9] = 1;
|
|
479
|
-
|
|
480
|
-
// call default touch handler so normal touch events still work
|
|
481
|
-
handleTouchDefault(e);
|
|
482
478
|
return;
|
|
483
479
|
}
|
|
484
480
|
}
|
|
@@ -509,12 +505,6 @@ function touchInputInit()
|
|
|
509
505
|
touchGamepadButtons[9] = 1;
|
|
510
506
|
}
|
|
511
507
|
}
|
|
512
|
-
|
|
513
|
-
// call default touch handler so normal touch events still work
|
|
514
|
-
handleTouchDefault(e);
|
|
515
|
-
|
|
516
|
-
// must return true so the document will get focus
|
|
517
|
-
return true;
|
|
518
508
|
}
|
|
519
509
|
}
|
|
520
510
|
|
|
@@ -524,7 +514,7 @@ function touchGamepadRender()
|
|
|
524
514
|
if (!touchInputEnable || !isTouchDevice || headlessMode) return;
|
|
525
515
|
if (!touchGamepadEnable || !touchGamepadTimer.isSet())
|
|
526
516
|
return;
|
|
527
|
-
|
|
517
|
+
|
|
528
518
|
// fade off when not touching or paused
|
|
529
519
|
const alpha = percent(touchGamepadTimer.get(), 4, 3);
|
|
530
520
|
if (!alpha || paused)
|
|
@@ -550,16 +540,16 @@ function touchGamepadRender()
|
|
|
550
540
|
}
|
|
551
541
|
else // draw cross shaped gamepad
|
|
552
542
|
{
|
|
553
|
-
for(let i=10; i--;)
|
|
543
|
+
for (let i=10; i--;)
|
|
554
544
|
{
|
|
555
545
|
const angle = i*PI/4;
|
|
556
546
|
context.arc(leftCenter.x, leftCenter.y,touchGamepadSize*.6, angle + PI/8, angle + PI/8);
|
|
557
547
|
i%2 && context.arc(leftCenter.x, leftCenter.y, touchGamepadSize*.33, angle, angle);
|
|
558
|
-
i
|
|
548
|
+
i===1 && context.fill();
|
|
559
549
|
}
|
|
560
550
|
context.stroke();
|
|
561
551
|
}
|
|
562
|
-
|
|
552
|
+
|
|
563
553
|
// draw right face buttons
|
|
564
554
|
const rightCenter = vec2(mainCanvasSize.x-touchGamepadSize, mainCanvasSize.y-touchGamepadSize);
|
|
565
555
|
for (let i=4; i--;)
|
|
@@ -594,4 +584,4 @@ function pointerLockExit() { document.exitPointerLock && document.exitPointerLoc
|
|
|
594
584
|
/** Check if pointer is locked (true if locked)
|
|
595
585
|
* @return {boolean}
|
|
596
586
|
* @memberof Input */
|
|
597
|
-
function pointerLockIsActive() { return document.pointerLockElement
|
|
587
|
+
function pointerLockIsActive() { return document.pointerLockElement === mainCanvas; }
|
package/src/engineMedals.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/**
|
|
1
|
+
/**
|
|
2
2
|
* LittleJS Medal System
|
|
3
3
|
* - Tracks and displays medals
|
|
4
4
|
* - Saves medals to local storage
|
|
@@ -21,7 +21,7 @@ let medalsDisplayQueue = [], medalsSaveName, medalsDisplayTimeLast;
|
|
|
21
21
|
/** Initialize medals with a save name used for storage
|
|
22
22
|
* - Call this after creating all medals
|
|
23
23
|
* - Checks if medals are unlocked
|
|
24
|
-
* @param {
|
|
24
|
+
* @param {string} saveName
|
|
25
25
|
* @memberof Medals */
|
|
26
26
|
function medalsInit(saveName)
|
|
27
27
|
{
|
|
@@ -36,7 +36,7 @@ function medalsInit(saveName)
|
|
|
36
36
|
{
|
|
37
37
|
if (!medalsDisplayQueue.length)
|
|
38
38
|
return;
|
|
39
|
-
|
|
39
|
+
|
|
40
40
|
// update first medal in queue
|
|
41
41
|
const medal = medalsDisplayQueue[0];
|
|
42
42
|
const time = timeReal - medalsDisplayTimeLast;
|
|
@@ -51,7 +51,7 @@ function medalsInit(saveName)
|
|
|
51
51
|
{
|
|
52
52
|
// slide on/off medals
|
|
53
53
|
const slideOffTime = medalDisplayTime - medalDisplaySlideTime;
|
|
54
|
-
const hidePercent =
|
|
54
|
+
const hidePercent =
|
|
55
55
|
time < medalDisplaySlideTime ? 1 - time / medalDisplaySlideTime :
|
|
56
56
|
time > slideOffTime ? (time - slideOffTime) / medalDisplaySlideTime : 0;
|
|
57
57
|
medal.render(hidePercent);
|
|
@@ -67,43 +67,43 @@ function medalsForEach(callback)
|
|
|
67
67
|
|
|
68
68
|
///////////////////////////////////////////////////////////////////////////////
|
|
69
69
|
|
|
70
|
-
/**
|
|
71
|
-
* Medal - Tracks an unlockable medal
|
|
70
|
+
/**
|
|
71
|
+
* Medal - Tracks an unlockable medal
|
|
72
72
|
* @example
|
|
73
73
|
* // create a medal
|
|
74
74
|
* const medal_example = new Medal(0, 'Example Medal', 'More info about the medal goes here.', '🎖️');
|
|
75
|
-
*
|
|
75
|
+
*
|
|
76
76
|
* // initialize medals
|
|
77
77
|
* medalsInit('Example Game');
|
|
78
|
-
*
|
|
78
|
+
*
|
|
79
79
|
* // unlock the medal
|
|
80
80
|
* medal_example.unlock();
|
|
81
81
|
*/
|
|
82
82
|
class Medal
|
|
83
83
|
{
|
|
84
84
|
/** Create a medal object and adds it to the list of medals
|
|
85
|
-
* @param {
|
|
86
|
-
* @param {
|
|
87
|
-
* @param {
|
|
88
|
-
* @param {
|
|
89
|
-
* @param {
|
|
85
|
+
* @param {number} id - The unique identifier of the medal
|
|
86
|
+
* @param {string} name - Name of the medal
|
|
87
|
+
* @param {string} [description] - Description of the medal
|
|
88
|
+
* @param {string} [icon] - Icon for the medal
|
|
89
|
+
* @param {string} [src] - Image location for the medal
|
|
90
90
|
*/
|
|
91
91
|
constructor(id, name, description='', icon='🏆', src)
|
|
92
92
|
{
|
|
93
93
|
ASSERT(id >= 0 && !medals[id]);
|
|
94
|
-
|
|
95
|
-
/** @property {
|
|
94
|
+
|
|
95
|
+
/** @property {number} - The unique identifier of the medal */
|
|
96
96
|
this.id = id;
|
|
97
|
-
|
|
98
|
-
/** @property {
|
|
97
|
+
|
|
98
|
+
/** @property {string} - Name of the medal */
|
|
99
99
|
this.name = name;
|
|
100
|
-
|
|
101
|
-
/** @property {
|
|
100
|
+
|
|
101
|
+
/** @property {string} - Description of the medal */
|
|
102
102
|
this.description = description;
|
|
103
|
-
|
|
104
|
-
/** @property {
|
|
103
|
+
|
|
104
|
+
/** @property {string} - Icon for the medal */
|
|
105
105
|
this.icon = icon;
|
|
106
|
-
|
|
106
|
+
|
|
107
107
|
/** @property {boolean} - Is the medal unlocked? */
|
|
108
108
|
this.unlocked = false;
|
|
109
109
|
|
|
@@ -128,7 +128,7 @@ class Medal
|
|
|
128
128
|
}
|
|
129
129
|
|
|
130
130
|
/** Render a medal
|
|
131
|
-
* @param {
|
|
131
|
+
* @param {number} [hidePercent] - How much to slide the medal off screen
|
|
132
132
|
*/
|
|
133
133
|
render(hidePercent=0)
|
|
134
134
|
{
|
|
@@ -142,7 +142,7 @@ class Medal
|
|
|
142
142
|
context.save();
|
|
143
143
|
context.beginPath();
|
|
144
144
|
context.fillStyle = new Color(.9,.9,.9).toString();
|
|
145
|
-
context.strokeStyle =
|
|
145
|
+
context.strokeStyle = BLACK.toString();
|
|
146
146
|
context.lineWidth = 3;
|
|
147
147
|
context.rect(x, y, width, height);
|
|
148
148
|
context.fill();
|
|
@@ -159,17 +159,17 @@ class Medal
|
|
|
159
159
|
const descriptionSize = height*.3;
|
|
160
160
|
const pos = vec2(x + medalDisplayIconSize + 2*gap.x, y + gap.y*2 + nameSize/2);
|
|
161
161
|
const textWidth = width - medalDisplayIconSize - 3*gap.x;
|
|
162
|
-
drawTextScreen(this.name, pos, nameSize,
|
|
162
|
+
drawTextScreen(this.name, pos, nameSize, BLACK, 0, undefined, 'left', undefined, textWidth);
|
|
163
163
|
|
|
164
164
|
// draw the description
|
|
165
165
|
pos.y = y + height - gap.y*2 - descriptionSize/2;
|
|
166
|
-
drawTextScreen(this.description, pos, descriptionSize,
|
|
166
|
+
drawTextScreen(this.description, pos, descriptionSize, BLACK, 0, undefined, 'left', undefined, textWidth);
|
|
167
167
|
context.restore();
|
|
168
168
|
}
|
|
169
169
|
|
|
170
170
|
/** Render the icon for a medal
|
|
171
171
|
* @param {Vector2} pos - Screen space position
|
|
172
|
-
* @param {
|
|
172
|
+
* @param {number} size - Screen space size
|
|
173
173
|
*/
|
|
174
174
|
renderIcon(pos, size)
|
|
175
175
|
{
|
|
@@ -177,9 +177,9 @@ class Medal
|
|
|
177
177
|
if (this.image)
|
|
178
178
|
overlayContext.drawImage(this.image, pos.x-size/2, pos.y-size/2, size, size);
|
|
179
179
|
else
|
|
180
|
-
drawTextScreen(this.icon, pos, size*.7,
|
|
180
|
+
drawTextScreen(this.icon, pos, size*.7, BLACK);
|
|
181
181
|
}
|
|
182
|
-
|
|
182
|
+
|
|
183
183
|
// Get local storage key used by the medal
|
|
184
184
|
storageKey() { return medalsSaveName + '_' + this.id; }
|
|
185
185
|
}
|