littlejsengine 1.13.4 → 1.14.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 -3
- package/dist/littlejs.d.ts +395 -249
- package/dist/littlejs.esm.js +1550 -754
- package/dist/littlejs.esm.min.js +1 -1
- package/dist/littlejs.js +1528 -744
- package/dist/littlejs.min.js +1 -1
- package/dist/littlejs.release.js +1504 -720
- package/examples/box2d/game.js +4 -4
- package/examples/box2d/gameObjects.js +1 -1
- package/examples/breakout/game.js +30 -25
- package/examples/breakoutTutorial/README.md +1 -1
- package/examples/breakoutTutorial/game.js +1 -1
- package/examples/electron/build.js +1 -1
- package/examples/electron/index.html +2 -2
- package/examples/empty/game.js +1 -1
- package/examples/htmlMenu/index.html +7 -7
- package/examples/index.html +208 -97
- package/examples/platformer/gameEffects.js +20 -25
- package/examples/platformer/gameLevel.js +6 -1
- package/examples/shorts/base.html +1 -1
- package/examples/shorts/flappyGame.js +2 -1
- package/examples/shorts/helloWorld.js +1 -1
- package/examples/shorts/music.js +106 -0
- package/examples/shorts/parallax.js +71 -0
- package/examples/shorts/piano.js +7 -8
- package/examples/shorts/postProcess.js +25 -8
- package/examples/shorts/shapes.js +12 -18
- package/examples/shorts/song.mp3 +0 -0
- package/examples/shorts/uiSystem.js +15 -8
- package/examples/starter/index.html +2 -2
- package/examples/stress/index.html +14 -7
- package/examples/style.css +14 -4
- package/examples/typescript/build.js +1 -1
- package/examples/uiSystem/game.js +11 -11
- package/package.json +1 -1
- package/plugins/box2d.js +12 -10
- package/plugins/drawUtilities.js +5 -5
- package/plugins/newgrounds.js +6 -6
- package/plugins/pluginExport.js +1 -1
- package/plugins/uiSystem.js +103 -79
- package/plugins/zzfxm.js +10 -10
- package/reference.md +94 -56
- package/src/engine.js +28 -24
- package/src/engineAudio.js +284 -109
- package/src/engineBuild.js +11 -11
- package/src/engineDebug.js +29 -29
- package/src/engineDraw.js +285 -112
- package/src/engineExport.js +28 -16
- package/src/engineInput.js +57 -67
- package/src/engineMedals.js +25 -25
- package/src/engineObject.js +28 -25
- package/src/engineParticles.js +59 -59
- package/src/engineRelease.js +1 -1
- package/src/engineSettings.js +20 -13
- package/src/engineTileLayer.js +34 -35
- package/src/engineUtilities.js +66 -59
- package/src/engineWebGL.js +466 -66
- /package/examples/shorts/{playSound.js → sound.js} +0 -0
package/src/engineExport.js
CHANGED
|
@@ -193,11 +193,14 @@ export
|
|
|
193
193
|
|
|
194
194
|
// Draw
|
|
195
195
|
textureInfos,
|
|
196
|
+
drawCount,
|
|
196
197
|
tile,
|
|
197
198
|
TileInfo,
|
|
198
199
|
TextureInfo,
|
|
199
200
|
mainCanvas,
|
|
200
201
|
mainContext,
|
|
202
|
+
drawCanvas,
|
|
203
|
+
drawContext,
|
|
201
204
|
overlayCanvas,
|
|
202
205
|
overlayContext,
|
|
203
206
|
mainCanvasSize,
|
|
@@ -205,6 +208,8 @@ export
|
|
|
205
208
|
worldToScreen,
|
|
206
209
|
drawTile,
|
|
207
210
|
drawRect,
|
|
211
|
+
drawRectGradient,
|
|
212
|
+
drawLineList,
|
|
208
213
|
drawLine,
|
|
209
214
|
drawPoly,
|
|
210
215
|
drawEllipse,
|
|
@@ -225,27 +230,33 @@ export
|
|
|
225
230
|
// WebGL
|
|
226
231
|
glCanvas,
|
|
227
232
|
glContext,
|
|
233
|
+
glClearCanvas,
|
|
234
|
+
glSetTexture,
|
|
228
235
|
glCompileShader,
|
|
229
|
-
glCopyToContext,
|
|
230
236
|
glCreateProgram,
|
|
231
237
|
glCreateTexture,
|
|
232
238
|
glDeleteTexture,
|
|
233
239
|
glSetTextureData,
|
|
234
|
-
glDraw,
|
|
235
240
|
glFlush,
|
|
236
|
-
|
|
241
|
+
glCopyToContext,
|
|
237
242
|
glSetAntialias,
|
|
238
|
-
|
|
243
|
+
glDraw,
|
|
244
|
+
glDrawPointsTransform,
|
|
245
|
+
glDrawOutlineTransform,
|
|
246
|
+
glDrawPoints,
|
|
247
|
+
glDrawColoredPoints,
|
|
239
248
|
glAntialias,
|
|
240
|
-
glShader,
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
glPositionData,
|
|
245
|
-
glColorData,
|
|
246
|
-
glInstanceCount,
|
|
247
|
-
glAdditive,
|
|
249
|
+
glShader,
|
|
250
|
+
glPolyShader,
|
|
251
|
+
glPolyMode,
|
|
252
|
+
glAdditive,
|
|
248
253
|
glBatchAdditive,
|
|
254
|
+
glActiveTexture,
|
|
255
|
+
glArrayBuffer,
|
|
256
|
+
glGeometryBuffer,
|
|
257
|
+
glPositionData,
|
|
258
|
+
glColorData,
|
|
259
|
+
glBatchCount,
|
|
249
260
|
|
|
250
261
|
// Input
|
|
251
262
|
keyIsDown,
|
|
@@ -278,17 +289,18 @@ export
|
|
|
278
289
|
pointerLockIsActive,
|
|
279
290
|
|
|
280
291
|
// Audio
|
|
292
|
+
audioContext,
|
|
293
|
+
audioMasterGain,
|
|
294
|
+
audioDefaultSampleRate,
|
|
281
295
|
Sound,
|
|
282
296
|
SoundWave,
|
|
283
|
-
|
|
297
|
+
SoundInstance,
|
|
284
298
|
speak,
|
|
285
299
|
speakStop,
|
|
286
300
|
getNoteFrequency,
|
|
287
301
|
playSamples,
|
|
288
302
|
zzfx,
|
|
289
303
|
zzfxG,
|
|
290
|
-
zzfxR,
|
|
291
|
-
audioContext,
|
|
292
304
|
|
|
293
305
|
// Base Object
|
|
294
306
|
EngineObject,
|
|
@@ -313,4 +325,4 @@ export
|
|
|
313
325
|
medalsPreventUnlock,
|
|
314
326
|
medalsInit,
|
|
315
327
|
Medal,
|
|
316
|
-
}
|
|
328
|
+
}
|
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)
|
|
@@ -555,11 +545,11 @@ function touchGamepadRender()
|
|
|
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
|
{
|
|
@@ -169,7 +169,7 @@ class Medal
|
|
|
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
|
{
|
|
@@ -179,7 +179,7 @@ class Medal
|
|
|
179
179
|
else
|
|
180
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
|
}
|
package/src/engineObject.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
/**
|
|
1
|
+
/**
|
|
2
2
|
* LittleJS Object System
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
'use strict';
|
|
6
6
|
|
|
7
|
-
/**
|
|
7
|
+
/**
|
|
8
8
|
* LittleJS Object Base Object Class
|
|
9
9
|
* - Top level object class used by the engine
|
|
10
10
|
* - Automatically adds self to object list
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
* @example
|
|
28
28
|
* // create an engine object, normally you would first extend the class with your own
|
|
29
29
|
* const pos = vec2(2,3);
|
|
30
|
-
* const object = new EngineObject(pos);
|
|
30
|
+
* const object = new EngineObject(pos);
|
|
31
31
|
*/
|
|
32
32
|
class EngineObject
|
|
33
33
|
{
|
|
@@ -42,12 +42,12 @@ class EngineObject
|
|
|
42
42
|
constructor(pos=vec2(), size=vec2(1), tileInfo, angle=0, color=new Color, renderOrder=0)
|
|
43
43
|
{
|
|
44
44
|
// check passed in params
|
|
45
|
-
ASSERT(isVector2(pos)
|
|
46
|
-
ASSERT(isVector2(size)
|
|
45
|
+
ASSERT(isVector2(pos), 'object pos should be a vec2');
|
|
46
|
+
ASSERT(isVector2(size), 'object size should be a vec2');
|
|
47
47
|
ASSERT(!tileInfo || tileInfo instanceof TileInfo, 'object tileInfo should be a TileInfo or undefined');
|
|
48
|
-
ASSERT(typeof angle
|
|
49
|
-
ASSERT(isColor(color)
|
|
50
|
-
ASSERT(typeof renderOrder
|
|
48
|
+
ASSERT(typeof angle === 'number' && isFinite(angle), 'object angle should be a number');
|
|
49
|
+
ASSERT(isColor(color), 'object color should be a valid rgba color');
|
|
50
|
+
ASSERT(typeof renderOrder === 'number', 'object renderOrder should be a number');
|
|
51
51
|
|
|
52
52
|
/** @property {Vector2} - World space position of the object */
|
|
53
53
|
this.pos = pos.copy();
|
|
@@ -115,7 +115,7 @@ class EngineObject
|
|
|
115
115
|
// add to list of objects
|
|
116
116
|
engineObjects.push(this);
|
|
117
117
|
}
|
|
118
|
-
|
|
118
|
+
|
|
119
119
|
/** Update the object transform, called automatically by engine even when paused */
|
|
120
120
|
updateTransforms()
|
|
121
121
|
{
|
|
@@ -184,7 +184,7 @@ class EngineObject
|
|
|
184
184
|
for (const o of engineObjectsCollide)
|
|
185
185
|
{
|
|
186
186
|
// non solid objects don't collide with each other
|
|
187
|
-
if (!this.isSolid && !o.isSolid || o.destroyed || o.parent || o
|
|
187
|
+
if (!this.isSolid && !o.isSolid || o.destroyed || o.parent || o === this)
|
|
188
188
|
continue;
|
|
189
189
|
|
|
190
190
|
// check collision
|
|
@@ -207,7 +207,7 @@ class EngineObject
|
|
|
207
207
|
this.velocity = this.velocity.add(velocity);
|
|
208
208
|
if (o.mass) // push away if not fixed
|
|
209
209
|
o.velocity = o.velocity.subtract(velocity);
|
|
210
|
-
|
|
210
|
+
|
|
211
211
|
debugPhysics && debugOverlap(this.pos, this.size, o.pos, o.size, '#f00');
|
|
212
212
|
continue;
|
|
213
213
|
}
|
|
@@ -218,7 +218,7 @@ class EngineObject
|
|
|
218
218
|
const isBlockedX = abs(oldPos.y - o.pos.y)*2 < sizeBoth.y;
|
|
219
219
|
const isBlockedY = abs(oldPos.x - o.pos.x)*2 < sizeBoth.x;
|
|
220
220
|
const restitution = max(this.restitution, o.restitution);
|
|
221
|
-
|
|
221
|
+
|
|
222
222
|
if (smallStepUp || isBlockedY || !isBlockedX) // resolve y collision
|
|
223
223
|
{
|
|
224
224
|
// push outside object collision
|
|
@@ -306,7 +306,7 @@ class EngineObject
|
|
|
306
306
|
{
|
|
307
307
|
// move to previous position
|
|
308
308
|
this.pos.y = oldPos.y;
|
|
309
|
-
this.groundObject = undefined;
|
|
309
|
+
this.groundObject = undefined;
|
|
310
310
|
}
|
|
311
311
|
}
|
|
312
312
|
if (blockedLayerX)
|
|
@@ -320,20 +320,20 @@ class EngineObject
|
|
|
320
320
|
}
|
|
321
321
|
}
|
|
322
322
|
}
|
|
323
|
-
|
|
323
|
+
|
|
324
324
|
/** Render the object, draws a tile by default, automatically called each frame, sorted by renderOrder */
|
|
325
325
|
render()
|
|
326
326
|
{
|
|
327
327
|
// default object render
|
|
328
328
|
drawTile(this.pos, this.drawSize || this.size, this.tileInfo, this.color, this.angle, this.mirror, this.additiveColor);
|
|
329
329
|
}
|
|
330
|
-
|
|
330
|
+
|
|
331
331
|
/** Destroy this object, destroy its children, detach it's parent, and mark it for removal */
|
|
332
332
|
destroy()
|
|
333
|
-
{
|
|
333
|
+
{
|
|
334
334
|
if (this.destroyed)
|
|
335
335
|
return;
|
|
336
|
-
|
|
336
|
+
|
|
337
337
|
// disconnect from parent and destroy children
|
|
338
338
|
this.destroyed = 1;
|
|
339
339
|
this.parent && this.parent.removeChild(this);
|
|
@@ -359,7 +359,7 @@ class EngineObject
|
|
|
359
359
|
/** Convert from world space to local space for a vector (rotation only)
|
|
360
360
|
* @param {Vector2} vec - world space vector */
|
|
361
361
|
worldToLocalVector(vec) { return vec.rotate(-this.angle); }
|
|
362
|
-
|
|
362
|
+
|
|
363
363
|
/** Called to check if a tile collision should be resolved
|
|
364
364
|
* @param {number} tileData - the value of the tile at the position
|
|
365
365
|
* @param {Vector2} pos - tile where the collision occurred
|
|
@@ -378,16 +378,19 @@ class EngineObject
|
|
|
378
378
|
|
|
379
379
|
/** Apply acceleration to this object (adjust velocity, not affected by mass)
|
|
380
380
|
* @param {Vector2} acceleration */
|
|
381
|
-
applyAcceleration(acceleration)
|
|
381
|
+
applyAcceleration(acceleration)
|
|
382
|
+
{ if (this.mass) this.velocity = this.velocity.add(acceleration); }
|
|
382
383
|
|
|
383
|
-
/** Apply angular acceleration to this object
|
|
384
|
+
/** Apply angular acceleration to this object
|
|
384
385
|
* @param {number} acceleration */
|
|
385
|
-
applyAngularAcceleration(acceleration)
|
|
386
|
+
applyAngularAcceleration(acceleration)
|
|
387
|
+
{ if (this.mass) this.angleVelocity += acceleration; }
|
|
386
388
|
|
|
387
389
|
/** Apply force to this object (adjust velocity, affected by mass)
|
|
388
390
|
* @param {Vector2} force */
|
|
389
|
-
applyForce(force)
|
|
390
|
-
|
|
391
|
+
applyForce(force)
|
|
392
|
+
{ if (this.mass) this.applyAcceleration(force.scale(1/this.mass)); }
|
|
393
|
+
|
|
391
394
|
/** Get the direction of the mirror
|
|
392
395
|
* @return {number} -1 if this.mirror is true, or 1 if not mirrored */
|
|
393
396
|
getMirrorSign() { return this.mirror ? -1 : 1; }
|
|
@@ -409,7 +412,7 @@ class EngineObject
|
|
|
409
412
|
* @param {EngineObject} child */
|
|
410
413
|
removeChild(child)
|
|
411
414
|
{
|
|
412
|
-
ASSERT(child.parent
|
|
415
|
+
ASSERT(child.parent === this && this.children.includes(child));
|
|
413
416
|
this.children.splice(this.children.indexOf(child), 1);
|
|
414
417
|
child.parent = 0;
|
|
415
418
|
}
|
|
@@ -455,7 +458,7 @@ class EngineObject
|
|
|
455
458
|
{
|
|
456
459
|
if (!debug)
|
|
457
460
|
return;
|
|
458
|
-
|
|
461
|
+
|
|
459
462
|
// show object info for debugging
|
|
460
463
|
const size = vec2(max(this.size.x, .2), max(this.size.y, .2));
|
|
461
464
|
const color = rgb(this.collideTiles?1:0, this.collideSolidObjects?1:0, this.isSolid?1:0, .5);
|