littlejsengine 1.18.4 → 1.18.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/COPYRIGHT.txt +38 -0
- package/FAQ.md +633 -0
- package/README.md +19 -1
- package/dist/littlejs.d.ts +349 -54
- package/dist/littlejs.esm.js +1469 -448
- package/dist/littlejs.esm.min.js +1 -1
- package/dist/littlejs.js +1438 -436
- package/dist/littlejs.min.js +1 -1
- package/dist/littlejs.release.js +1438 -436
- package/package.json +8 -1
- package/plugins/box2d.js +32 -7
- package/{src/engineMedals.js → plugins/medalSystem.js} +248 -196
- package/plugins/pathFinder.js +758 -0
- package/plugins/pluginExport.js +19 -0
- package/plugins/tween.js +1 -1
- package/plugins/tweenSystem.js +509 -0
- package/plugins/uiSystem.js +150 -11
- package/src/engine.js +2 -141
- package/src/engineAudio.js +8 -5
- package/src/engineBuild.mjs +4 -2
- package/src/engineDebug.js +1 -1
- package/src/engineDraw.js +21 -3
- package/src/engineExport.js +12 -12
- package/src/engineInput.js +1 -1
- package/src/engineLogo.js +146 -0
- package/src/engineMath.js +10 -3
- package/src/engineObject.js +11 -5
- package/src/engineParticles.js +6 -3
- package/src/engineRelease.js +1 -1
- package/src/engineSettings.js +0 -47
- package/src/engineUtilities.js +7 -2
- package/src/engineWebGL.js +34 -7
- package/src/jsconfig.json +3 -1
- package/.github/workflows/test.yml +0 -17
- package/AI.md +0 -173
- package/CLAUDE.md +0 -1
- package/examples/box2d/game.js +0 -192
- package/examples/box2d/gameObjects.js +0 -564
- package/examples/box2d/index.html +0 -9
- package/examples/box2d/scenes.js +0 -194
- package/examples/box2d/tiles.png +0 -0
- package/examples/breakout/game.js +0 -175
- package/examples/breakout/gameObjects.js +0 -147
- package/examples/breakout/index.html +0 -8
- package/examples/breakout/tiles.png +0 -0
- package/examples/breakoutTutorial/README.md +0 -521
- package/examples/breakoutTutorial/game.js +0 -191
- package/examples/breakoutTutorial/images/1.png +0 -0
- package/examples/breakoutTutorial/images/10.png +0 -0
- package/examples/breakoutTutorial/images/11.png +0 -0
- package/examples/breakoutTutorial/images/2.png +0 -0
- package/examples/breakoutTutorial/images/3.png +0 -0
- package/examples/breakoutTutorial/images/4.png +0 -0
- package/examples/breakoutTutorial/images/5.png +0 -0
- package/examples/breakoutTutorial/images/6.png +0 -0
- package/examples/breakoutTutorial/images/7.png +0 -0
- package/examples/breakoutTutorial/images/8.png +0 -0
- package/examples/breakoutTutorial/images/9.png +0 -0
- package/examples/breakoutTutorial/index.html +0 -8
- package/examples/electron/build.mjs +0 -127
- package/examples/electron/electron.js +0 -35
- package/examples/electron/game.js +0 -54
- package/examples/electron/index.html +0 -13
- package/examples/electron/package.json +0 -5
- package/examples/electron/tiles.png +0 -0
- package/examples/empty/game.js +0 -51
- package/examples/empty/index.html +0 -5
- package/examples/empty/tiles.png +0 -0
- package/examples/favicon.png +0 -0
- package/examples/games.jpg +0 -0
- package/examples/htmlMenu/game.js +0 -82
- package/examples/htmlMenu/index.html +0 -45
- package/examples/htmlMenu/tiles.png +0 -0
- package/examples/index.html +0 -62
- package/examples/logo.png +0 -0
- package/examples/logo2.png +0 -0
- package/examples/module/build.mjs +0 -124
- package/examples/module/game.js +0 -132
- package/examples/module/index.html +0 -10
- package/examples/module/tiles.png +0 -0
- package/examples/particles/index.html +0 -426
- package/examples/particles/tiles.png +0 -0
- package/examples/platformer/data/gameLevelData.tmx +0 -143
- package/examples/platformer/data/gameLevelData.tsx +0 -4
- package/examples/platformer/game.js +0 -147
- package/examples/platformer/gameCharacter.js +0 -309
- package/examples/platformer/gameEffects.js +0 -278
- package/examples/platformer/gameLevel.js +0 -205
- package/examples/platformer/gameLevelData.json +0 -171
- package/examples/platformer/gameObjects.js +0 -378
- package/examples/platformer/gamePlayer.js +0 -35
- package/examples/platformer/index.html +0 -9
- package/examples/platformer/tiles.png +0 -0
- package/examples/platformer/tilesLevel.png +0 -0
- package/examples/puzzle/game.js +0 -331
- package/examples/puzzle/index.html +0 -8
- package/examples/puzzle/tiles.png +0 -0
- package/examples/screenshot.jpg +0 -0
- package/examples/shorts/animation.js +0 -18
- package/examples/shorts/base.html +0 -54
- package/examples/shorts/blending.js +0 -14
- package/examples/shorts/box2d.js +0 -51
- package/examples/shorts/box2dCar.js +0 -59
- package/examples/shorts/box2dPool.js +0 -107
- package/examples/shorts/box2dTileLayer.js +0 -48
- package/examples/shorts/cameraDrag.js +0 -22
- package/examples/shorts/clock.js +0 -21
- package/examples/shorts/colors.js +0 -25
- package/examples/shorts/debugDraw.js +0 -37
- package/examples/shorts/empty.js +0 -30
- package/examples/shorts/flappyGame.js +0 -55
- package/examples/shorts/fontImage.js +0 -17
- package/examples/shorts/fps.js +0 -90
- package/examples/shorts/helloWorld.js +0 -11
- package/examples/shorts/hillGlideGame.js +0 -63
- package/examples/shorts/input.js +0 -64
- package/examples/shorts/landerGame.js +0 -57
- package/examples/shorts/maze.js +0 -48
- package/examples/shorts/medals.js +0 -51
- package/examples/shorts/music.js +0 -78
- package/examples/shorts/musicPlayer.js +0 -137
- package/examples/shorts/nineSlice.js +0 -40
- package/examples/shorts/parallax.js +0 -72
- package/examples/shorts/particles.js +0 -28
- package/examples/shorts/piano.js +0 -44
- package/examples/shorts/platformer.js +0 -45
- package/examples/shorts/pongGame.js +0 -41
- package/examples/shorts/postProcess.js +0 -62
- package/examples/shorts/sequencer.js +0 -124
- package/examples/shorts/shader.js +0 -29
- package/examples/shorts/shapes.js +0 -21
- package/examples/shorts/slidingPuzzle.js +0 -52
- package/examples/shorts/song.mp3 +0 -0
- package/examples/shorts/sound.js +0 -36
- package/examples/shorts/spaceGame.js +0 -58
- package/examples/shorts/spriteAtlas.js +0 -31
- package/examples/shorts/starfield.js +0 -15
- package/examples/shorts/texture.js +0 -16
- package/examples/shorts/textureWrapped.js +0 -12
- package/examples/shorts/tileLayer.js +0 -48
- package/examples/shorts/tileRaycast.js +0 -39
- package/examples/shorts/tiles.png +0 -0
- package/examples/shorts/tiltedView.js +0 -63
- package/examples/shorts/timers.js +0 -53
- package/examples/shorts/topDown.js +0 -38
- package/examples/shorts/tween.js +0 -24
- package/examples/shorts/uiSystem.js +0 -61
- package/examples/shorts/video.webm +0 -0
- package/examples/shorts/videoPlayer.js +0 -34
- package/examples/shorts.js +0 -743
- package/examples/starter/build.bat +0 -7
- package/examples/starter/build.mjs +0 -126
- package/examples/starter/game.js +0 -137
- package/examples/starter/index.html +0 -35
- package/examples/starter/tiles.png +0 -0
- package/examples/stress/index.html +0 -173
- package/examples/style.css +0 -150
- package/examples/tweenSystem/game.js +0 -171
- package/examples/tweenSystem/index.html +0 -10
- package/examples/tweenSystem/tiles.png +0 -0
- package/examples/typescript/build.mjs +0 -60
- package/examples/typescript/game.js +0 -100
- package/examples/typescript/game.ts +0 -132
- package/examples/typescript/index.html +0 -10
- package/examples/typescript/tiles.png +0 -0
- package/examples/typescript/tsconfig.json +0 -17
- package/examples/uiSystem/game.js +0 -139
- package/examples/uiSystem/index.html +0 -10
- package/examples/uiSystem/tiles.png +0 -0
- package/jsconfig.json +0 -12
- package/plugins/desktop.ini +0 -2
- package/reference.md +0 -448
- package/test/math.test.mjs +0 -774
- package/test/setup.mjs +0 -22
- package/test/smoke.test.mjs +0 -274
- package/test/tween.test.mjs +0 -576
- package/test/util.test.mjs +0 -80
package/plugins/uiSystem.js
CHANGED
|
@@ -118,16 +118,13 @@ class UISystemPlugin
|
|
|
118
118
|
this.lastHoverObject = undefined;
|
|
119
119
|
/** @property {UIObject} - Current confirm menu being shown */
|
|
120
120
|
this.confirmDialog = undefined;
|
|
121
|
-
/** @
|
|
122
|
-
this.
|
|
121
|
+
/** @private */
|
|
122
|
+
this._keyInputObject = undefined;
|
|
123
|
+
/** @private */
|
|
124
|
+
this._onKeyDown = (e) => this._keyInputObject?.onKeyDown(e);
|
|
123
125
|
|
|
124
126
|
engineAddPlugin(uiUpdate, uiRender);
|
|
125
127
|
|
|
126
|
-
// key down handler
|
|
127
|
-
function onKeyDown(e)
|
|
128
|
-
{ uiSystem.keyInputObject?.onKeyDown(e); }
|
|
129
|
-
document.addEventListener('keydown', onKeyDown);
|
|
130
|
-
|
|
131
128
|
// set object position in parent space
|
|
132
129
|
function updateTransforms(o)
|
|
133
130
|
{
|
|
@@ -469,8 +466,23 @@ class UISystemPlugin
|
|
|
469
466
|
return p;
|
|
470
467
|
}
|
|
471
468
|
|
|
469
|
+
/** Object to send keyboard input to (typically a UITextInput).
|
|
470
|
+
* The document keydown listener is only attached while this is set,
|
|
471
|
+
* so games that never use text input pay no event-handling cost.
|
|
472
|
+
* @type {UIObject} */
|
|
473
|
+
get keyInputObject() { return this._keyInputObject; }
|
|
474
|
+
set keyInputObject(obj)
|
|
475
|
+
{
|
|
476
|
+
const had = !!this._keyInputObject;
|
|
477
|
+
this._keyInputObject = obj;
|
|
478
|
+
if (!had && obj)
|
|
479
|
+
document.addEventListener('keydown', this._onKeyDown);
|
|
480
|
+
else if (had && !obj)
|
|
481
|
+
document.removeEventListener('keydown', this._onKeyDown);
|
|
482
|
+
}
|
|
483
|
+
|
|
472
484
|
/** Destroy and remove all objects
|
|
473
|
-
* @memberof
|
|
485
|
+
* @memberof UISystem */
|
|
474
486
|
destroyObjects()
|
|
475
487
|
{
|
|
476
488
|
for (const o of this.uiObjects)
|
|
@@ -893,15 +905,13 @@ class UIObject
|
|
|
893
905
|
* @return {string} */
|
|
894
906
|
toString()
|
|
895
907
|
{
|
|
896
|
-
if (!debug) return;
|
|
897
|
-
|
|
898
908
|
let text = 'type = ' + this.constructor.name;
|
|
899
909
|
if (this.text)
|
|
900
910
|
text += '\ntext = ' + this.text;
|
|
901
911
|
if (this.pos.x || this.pos.y)
|
|
902
912
|
text += '\npos = ' + this.pos;
|
|
903
913
|
if (this.localPos.x || this.localPos.y)
|
|
904
|
-
text += '\
|
|
914
|
+
text += '\nlocalPos = ' + this.localPos;
|
|
905
915
|
if (this.size.x || this.size.y)
|
|
906
916
|
text += '\nsize = ' + this.size;
|
|
907
917
|
if (this.color)
|
|
@@ -1495,4 +1505,133 @@ class UIVideo extends UIObject
|
|
|
1495
1505
|
this.video.remove();
|
|
1496
1506
|
super.destroy();
|
|
1497
1507
|
}
|
|
1508
|
+
}
|
|
1509
|
+
|
|
1510
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
1511
|
+
/**
|
|
1512
|
+
* UILayout - A container that auto-arranges children in a vertical list, horizontal list, or grid
|
|
1513
|
+
* - Set columns to 1 for a vertical list (default)
|
|
1514
|
+
* - Set columns to children.length for a horizontal list
|
|
1515
|
+
* - Set columns to N (1 < N < children.length) for a grid with N columns
|
|
1516
|
+
* - Per-child sizing: each row's height = max child.size.y in that row, each column's width = max child.size.x in that column
|
|
1517
|
+
* - Children are positioned centered in their cell
|
|
1518
|
+
* - Container auto-sizes to fit children plus padding
|
|
1519
|
+
* @extends UIObject
|
|
1520
|
+
* @memberof UISystem
|
|
1521
|
+
*/
|
|
1522
|
+
class UILayout extends UIObject
|
|
1523
|
+
{
|
|
1524
|
+
/** Create a UILayout container that auto-arranges children
|
|
1525
|
+
* @param {Vector2} [pos]
|
|
1526
|
+
* @param {number} [columns=1] - Number of columns (1 = vertical list)
|
|
1527
|
+
* @param {number} [gap=10] - Space between children
|
|
1528
|
+
* @param {number} [padding=10] - Space between container border and children
|
|
1529
|
+
* @param {boolean} [transparent=false] - If true, draws no background, outline, or shadow
|
|
1530
|
+
*/
|
|
1531
|
+
constructor(pos, columns=1, gap=10, padding=10, transparent=false)
|
|
1532
|
+
{
|
|
1533
|
+
super(pos);
|
|
1534
|
+
|
|
1535
|
+
ASSERT(isNumber(columns) && columns >= 1, 'ui layout columns must be a number >= 1');
|
|
1536
|
+
ASSERT(isNumber(gap), 'ui layout gap must be a number');
|
|
1537
|
+
ASSERT(isNumber(padding), 'ui layout padding must be a number');
|
|
1538
|
+
|
|
1539
|
+
/** @property {number} - Number of columns in the layout */
|
|
1540
|
+
this.columns = columns;
|
|
1541
|
+
/** @property {number} - Space between children */
|
|
1542
|
+
this.gap = gap;
|
|
1543
|
+
/** @property {number} - Space between container border and children */
|
|
1544
|
+
this.padding = padding;
|
|
1545
|
+
|
|
1546
|
+
if (transparent)
|
|
1547
|
+
{
|
|
1548
|
+
// pure positioning helper - skip background, outline, and shadow
|
|
1549
|
+
this.color = CLEAR_BLACK;
|
|
1550
|
+
this.gradientColor = undefined;
|
|
1551
|
+
this.lineWidth = 0;
|
|
1552
|
+
this.shadowColor = CLEAR_BLACK;
|
|
1553
|
+
}
|
|
1554
|
+
this.relayout();
|
|
1555
|
+
}
|
|
1556
|
+
|
|
1557
|
+
/** Add a child UIObject and re-layout
|
|
1558
|
+
* @param {UIObject} child
|
|
1559
|
+
* @return {UIObject} The child object added */
|
|
1560
|
+
addChild(child)
|
|
1561
|
+
{
|
|
1562
|
+
super.addChild(child);
|
|
1563
|
+
this.relayout();
|
|
1564
|
+
return child;
|
|
1565
|
+
}
|
|
1566
|
+
|
|
1567
|
+
/** Remove a child UIObject and re-layout
|
|
1568
|
+
* @param {UIObject} child */
|
|
1569
|
+
removeChild(child)
|
|
1570
|
+
{
|
|
1571
|
+
super.removeChild(child);
|
|
1572
|
+
this.relayout();
|
|
1573
|
+
}
|
|
1574
|
+
|
|
1575
|
+
/** Recompute child positions and container size based on per-child sizes.
|
|
1576
|
+
* Called automatically by addChild and removeChild. Call manually if you
|
|
1577
|
+
* mutate a child's size or change columns, gap, or padding. */
|
|
1578
|
+
relayout()
|
|
1579
|
+
{
|
|
1580
|
+
const n = this.children.length;
|
|
1581
|
+
if (!n)
|
|
1582
|
+
{
|
|
1583
|
+
this.size = vec2(this.padding * 2);
|
|
1584
|
+
return;
|
|
1585
|
+
}
|
|
1586
|
+
|
|
1587
|
+
const cols = this.columns;
|
|
1588
|
+
const rows = ceil(n / cols);
|
|
1589
|
+
const colWidths = new Array(cols).fill(0);
|
|
1590
|
+
const rowHeights = new Array(rows).fill(0);
|
|
1591
|
+
|
|
1592
|
+
// first pass: compute column widths and row heights from child sizes
|
|
1593
|
+
for (let i = 0; i < n; ++i)
|
|
1594
|
+
{
|
|
1595
|
+
const col = i % cols;
|
|
1596
|
+
const row = floor(i / cols);
|
|
1597
|
+
const child = this.children[i];
|
|
1598
|
+
colWidths[col] = max(colWidths[col], child.size.x);
|
|
1599
|
+
rowHeights[row] = max(rowHeights[row], child.size.y);
|
|
1600
|
+
}
|
|
1601
|
+
|
|
1602
|
+
// total content size (sum of column widths/row heights plus gaps between them)
|
|
1603
|
+
let contentWidth = this.gap * (cols - 1);
|
|
1604
|
+
for (const w of colWidths) contentWidth += w;
|
|
1605
|
+
let contentHeight = this.gap * (rows - 1);
|
|
1606
|
+
for (const h of rowHeights) contentHeight += h;
|
|
1607
|
+
|
|
1608
|
+
// cumulative column/row offsets so positioning is O(n) not O(n^2)
|
|
1609
|
+
const colOffsets = new Array(cols);
|
|
1610
|
+
let xAcc = 0;
|
|
1611
|
+
for (let c = 0; c < cols; ++c)
|
|
1612
|
+
{
|
|
1613
|
+
colOffsets[c] = xAcc;
|
|
1614
|
+
xAcc += colWidths[c];
|
|
1615
|
+
}
|
|
1616
|
+
const rowOffsets = new Array(rows);
|
|
1617
|
+
let yAcc = 0;
|
|
1618
|
+
for (let r = 0; r < rows; ++r)
|
|
1619
|
+
{
|
|
1620
|
+
rowOffsets[r] = yAcc;
|
|
1621
|
+
yAcc += rowHeights[r];
|
|
1622
|
+
}
|
|
1623
|
+
|
|
1624
|
+
// second pass: position each child centered in its cell
|
|
1625
|
+
for (let i = 0; i < n; ++i)
|
|
1626
|
+
{
|
|
1627
|
+
const col = i % cols;
|
|
1628
|
+
const row = floor(i / cols);
|
|
1629
|
+
const x = -contentWidth/2 + colOffsets[col] + this.gap * col + colWidths[col] / 2;
|
|
1630
|
+
const y = -contentHeight/2 + rowOffsets[row] + this.gap * row + rowHeights[row] / 2;
|
|
1631
|
+
this.children[i].localPos = vec2(x, y);
|
|
1632
|
+
}
|
|
1633
|
+
|
|
1634
|
+
// container size = content + padding on all sides
|
|
1635
|
+
this.size = vec2(contentWidth + this.padding * 2, contentHeight + this.padding * 2);
|
|
1636
|
+
}
|
|
1498
1637
|
}
|
package/src/engine.js
CHANGED
|
@@ -32,7 +32,7 @@ const engineName = 'LittleJS';
|
|
|
32
32
|
* @type {string}
|
|
33
33
|
* @default
|
|
34
34
|
* @memberof Engine */
|
|
35
|
-
const engineVersion = '1.18.
|
|
35
|
+
const engineVersion = '1.18.7';
|
|
36
36
|
|
|
37
37
|
/** Frames per second to update
|
|
38
38
|
* @type {number}
|
|
@@ -204,7 +204,7 @@ async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, game
|
|
|
204
204
|
const combinedScale = timeScale * debugScale;
|
|
205
205
|
frameTimeDeltaMS *= combinedScale;
|
|
206
206
|
frameTimeBufferMS += paused ? 0 : frameTimeDeltaMS;
|
|
207
|
-
if (
|
|
207
|
+
if (debugScale <= 1)
|
|
208
208
|
frameTimeBufferMS = min(frameTimeBufferMS, 50); // clamp min framerate
|
|
209
209
|
|
|
210
210
|
let wasUpdated = false;
|
|
@@ -562,143 +562,4 @@ function engineObjectsRaycast(start, end, objects=engineObjects)
|
|
|
562
562
|
|
|
563
563
|
debugRaycast && debugLine(start, end, hitObjects.length ? '#f00' : '#00f', .02);
|
|
564
564
|
return hitObjects;
|
|
565
|
-
}
|
|
566
|
-
|
|
567
|
-
///////////////////////////////////////////////////////////////////////////////
|
|
568
|
-
function drawEngineLogo(t)
|
|
569
|
-
{
|
|
570
|
-
const blackAndWhite = 0;
|
|
571
|
-
const showName = 1;
|
|
572
|
-
|
|
573
|
-
// LittleJS Logo and Splash Screen
|
|
574
|
-
const x = mainContext;
|
|
575
|
-
const dpr = canvasPixelRatio ?? (devicePixelRatio || 1);
|
|
576
|
-
const w = mainCanvas.width = innerWidth * dpr;
|
|
577
|
-
const h = mainCanvas.height = innerHeight * dpr;
|
|
578
|
-
{
|
|
579
|
-
// background
|
|
580
|
-
const p3 = percent(t, 1, .8);
|
|
581
|
-
const p4 = percent(t, 0, .5);
|
|
582
|
-
const g = x.createRadialGradient(w/2,h/2,0,w/2,h/2,hypot(w,h)*.6);
|
|
583
|
-
g.addColorStop(0,hsl(0,0,lerp(0,p3/2,p4),p3).toString());
|
|
584
|
-
g.addColorStop(1,hsl(0,0,0,p3).toString());
|
|
585
|
-
x.save();
|
|
586
|
-
x.fillStyle = g;
|
|
587
|
-
x.fillRect(0,0,w,h);
|
|
588
|
-
}
|
|
589
|
-
const gradient = (X1,Y1,X2,Y2,C,S=1)=>
|
|
590
|
-
{
|
|
591
|
-
if (C >= 0)
|
|
592
|
-
{
|
|
593
|
-
if (blackAndWhite)
|
|
594
|
-
x.fillStyle = '#fff';
|
|
595
|
-
else
|
|
596
|
-
{
|
|
597
|
-
const g = x.fillStyle = x.createLinearGradient(X1,Y1,X2,Y2);
|
|
598
|
-
g.addColorStop(0,color(C,2));
|
|
599
|
-
g.addColorStop(1,color(C,1));
|
|
600
|
-
}
|
|
601
|
-
}
|
|
602
|
-
else
|
|
603
|
-
x.fillStyle = '#000';
|
|
604
|
-
C >= -1 ? (x.fill(), S && x.stroke()) : x.stroke();
|
|
605
|
-
}
|
|
606
|
-
const circle = (X,Y,R,A=0,B=2*PI,C,S)=>
|
|
607
|
-
{
|
|
608
|
-
x.beginPath();
|
|
609
|
-
x.arc(X,Y,R,p*A,p*B);
|
|
610
|
-
gradient(X,Y-R,X,Y+R,C,S);
|
|
611
|
-
}
|
|
612
|
-
const rect = (X,Y,W,H,C)=>
|
|
613
|
-
{
|
|
614
|
-
x.beginPath();
|
|
615
|
-
x.rect(X,Y,W,H*p);
|
|
616
|
-
gradient(X,Y+H,X+W,Y,C);
|
|
617
|
-
}
|
|
618
|
-
const poly = (points,C,Y,H)=>
|
|
619
|
-
{
|
|
620
|
-
x.beginPath();
|
|
621
|
-
for (const p of points)
|
|
622
|
-
x.lineTo(p.x, p.y);
|
|
623
|
-
x.closePath();
|
|
624
|
-
gradient(0, Y, 0, Y+H,C);
|
|
625
|
-
}
|
|
626
|
-
const color = (c,l)=> l?`hsl(${[.95,.56,.13][c%3]*360} 99%${[0,50,75][l]}%`:'#000';
|
|
627
|
-
|
|
628
|
-
// center and fit tos screen
|
|
629
|
-
const alpha = oscillate(1,1,t);
|
|
630
|
-
const p = percent(alpha, .1, .5);
|
|
631
|
-
const size = min(6, min(w,h)/99);
|
|
632
|
-
x.translate(w/2,h/2);
|
|
633
|
-
x.scale(size,size);
|
|
634
|
-
x.translate(-40,-35);
|
|
635
|
-
p < 1 && x.setLineDash([99*p,99]);
|
|
636
|
-
x.lineJoin = x.lineCap = 'round';
|
|
637
|
-
x.lineWidth = .1 + p*1.9;
|
|
638
|
-
//x.strokeStyle='#fff7';
|
|
639
|
-
|
|
640
|
-
if (showName)
|
|
641
|
-
{
|
|
642
|
-
// engine name text
|
|
643
|
-
const Y = 54;
|
|
644
|
-
const s = 'LittleJS';
|
|
645
|
-
x.font = '900 15.5px arial';
|
|
646
|
-
x.lineWidth = .1+p*3.9;
|
|
647
|
-
x.textAlign = 'center';
|
|
648
|
-
x.textBaseline = 'top';
|
|
649
|
-
rect(11,Y+1,59,8*p,-1);
|
|
650
|
-
x.beginPath();
|
|
651
|
-
|
|
652
|
-
let w2 = 0;
|
|
653
|
-
for (let i=0;i<s.length;++i)
|
|
654
|
-
w2 += x.measureText(s[i]).width;
|
|
655
|
-
for (let j=2;j--;)
|
|
656
|
-
for (let i=0,X=40-w2/2;i<s.length;++i)
|
|
657
|
-
{
|
|
658
|
-
const w = x.measureText(s[i]).width, X2 = X+w/2;
|
|
659
|
-
gradient(X2,Y,X2+2,Y+13,i>5?1:0);
|
|
660
|
-
x[j?'strokeText':'fillText'](s[i],X2,Y+.5,17*p);
|
|
661
|
-
X += w;
|
|
662
|
-
}
|
|
663
|
-
|
|
664
|
-
x.lineWidth = .1 + p*1.9;
|
|
665
|
-
rect(3,Y,73,0); // bottom
|
|
666
|
-
}
|
|
667
|
-
|
|
668
|
-
rect(7,15,26,-7,0); // cab top
|
|
669
|
-
rect(25,15,8,25,-1); // cab front
|
|
670
|
-
rect(10,40,15,-25,1); // cab back
|
|
671
|
-
rect(14,21,7,9,2); // cab window
|
|
672
|
-
rect(38,20,6,-6,2); // little stack
|
|
673
|
-
|
|
674
|
-
// big stack
|
|
675
|
-
rect(49,20,10,-6,0);
|
|
676
|
-
const stackPoints = [vec2(44,8),vec2(64,8),vec2(59,8+6*p),vec2(49,8+6*p)];
|
|
677
|
-
poly(stackPoints,2,8,6*p);
|
|
678
|
-
rect(44,8,20,-7,0);
|
|
679
|
-
|
|
680
|
-
// engine
|
|
681
|
-
for (let i=5;i--;) circle(59-i*6*p,30,10,0,2*PI,1,0);
|
|
682
|
-
circle(59,30,4,0,7,2); // light
|
|
683
|
-
|
|
684
|
-
// engine outline
|
|
685
|
-
rect(35,20,24,0); // top
|
|
686
|
-
circle(59,30,10); // front
|
|
687
|
-
circle(47,30,10,PI/2,PI*3/2); // middle
|
|
688
|
-
circle(35,30,10,PI/2,PI*3/2); // back
|
|
689
|
-
rect(7,40,13,7,-1); // bottom back
|
|
690
|
-
rect(17,40,43,14,-1); // bottom center
|
|
691
|
-
|
|
692
|
-
// wheels
|
|
693
|
-
for (let i=3;i--;) for (let j=2;j--;) circle(17+15*i,47,j?7:1,0,2*PI,2);
|
|
694
|
-
|
|
695
|
-
// cowcatcher
|
|
696
|
-
for (let i=2;i--;)
|
|
697
|
-
{
|
|
698
|
-
let w=6, s=7, o=53+w*p*i
|
|
699
|
-
const points = [vec2(o+s,54),vec2(o,40),vec2(o+w*p,40),vec2(o+s+w*p,54)];
|
|
700
|
-
poly(points,0,40,14);
|
|
701
|
-
}
|
|
702
|
-
|
|
703
|
-
x.restore();
|
|
704
565
|
}
|
package/src/engineAudio.js
CHANGED
|
@@ -14,10 +14,12 @@
|
|
|
14
14
|
|
|
15
15
|
'use strict';
|
|
16
16
|
|
|
17
|
-
/** Audio context used by the engine
|
|
17
|
+
/** Audio context used by the engine. Created lazily in audioInit() to avoid
|
|
18
|
+
* browser autoplay warnings about constructing an AudioContext before any
|
|
19
|
+
* user gesture.
|
|
18
20
|
* @type {AudioContext}
|
|
19
21
|
* @memberof Audio */
|
|
20
|
-
let audioContext
|
|
22
|
+
let audioContext;
|
|
21
23
|
|
|
22
24
|
/** Master gain node for all audio to pass through
|
|
23
25
|
* @type {GainNode}
|
|
@@ -33,12 +35,13 @@ const audioDefaultSampleRate = 44100;
|
|
|
33
35
|
* @return {boolean} - True if the audio context is running
|
|
34
36
|
* @memberof Audio */
|
|
35
37
|
function audioIsRunning()
|
|
36
|
-
{ return audioContext
|
|
38
|
+
{ return audioContext?.state === 'running'; }
|
|
37
39
|
|
|
38
40
|
function audioInit()
|
|
39
41
|
{
|
|
40
42
|
if (!soundEnable || headlessMode) return;
|
|
41
43
|
|
|
44
|
+
audioContext = new AudioContext;
|
|
42
45
|
audioMasterGain = audioContext.createGain();
|
|
43
46
|
audioMasterGain.connect(audioContext.destination);
|
|
44
47
|
audioMasterGain.gain.value = soundVolume; // set starting value
|
|
@@ -105,8 +108,8 @@ class Sound
|
|
|
105
108
|
|
|
106
109
|
if (Array.isArray(asset))
|
|
107
110
|
{
|
|
108
|
-
// generate zzfx sound
|
|
109
|
-
const zzfxSound = asset;
|
|
111
|
+
// generate zzfx sound — copy so we don't mutate the caller's array
|
|
112
|
+
const zzfxSound = asset.slice();
|
|
110
113
|
|
|
111
114
|
// remove randomness so it can be applied on playback
|
|
112
115
|
const defaultRandomness = randomness ?? .05;
|
package/src/engineBuild.mjs
CHANGED
|
@@ -33,18 +33,20 @@ const engineSourceFiles =
|
|
|
33
33
|
`${SOURCE_FOLDER}/engineAudio.js`,
|
|
34
34
|
`${SOURCE_FOLDER}/engineTileLayer.js`,
|
|
35
35
|
`${SOURCE_FOLDER}/engineParticles.js`,
|
|
36
|
-
`${SOURCE_FOLDER}/engineMedals.js`,
|
|
37
36
|
`${SOURCE_FOLDER}/engineWebGL.js`,
|
|
37
|
+
`${SOURCE_FOLDER}/engineLogo.js`,
|
|
38
38
|
];
|
|
39
39
|
const enginePluginFiles =
|
|
40
40
|
[
|
|
41
|
+
`${PLUGIN_FOLDER}/medalSystem.js`,
|
|
41
42
|
`${PLUGIN_FOLDER}/newgrounds.js`,
|
|
42
43
|
`${PLUGIN_FOLDER}/postProcess.js`,
|
|
43
44
|
`${PLUGIN_FOLDER}/zzfxm.js`,
|
|
44
45
|
`${PLUGIN_FOLDER}/uiSystem.js`,
|
|
45
46
|
`${PLUGIN_FOLDER}/box2d.js`,
|
|
46
47
|
`${PLUGIN_FOLDER}/drawUtilities.js`,
|
|
47
|
-
`${PLUGIN_FOLDER}/
|
|
48
|
+
`${PLUGIN_FOLDER}/tweenSystem.js`,
|
|
49
|
+
`${PLUGIN_FOLDER}/pathFinder.js`,
|
|
48
50
|
];
|
|
49
51
|
const engineExtraFiles =
|
|
50
52
|
[
|
package/src/engineDebug.js
CHANGED
|
@@ -44,7 +44,7 @@ let debugKey = 'Escape';
|
|
|
44
44
|
let debugOverlay = false;
|
|
45
45
|
|
|
46
46
|
// Engine internal variables not exposed to documentation
|
|
47
|
-
let debugPrimitives = [], debugPhysics = false, debugRaycast = false, debugParticles = false, debugGamepads = false,
|
|
47
|
+
let debugPrimitives = [], debugPhysics = false, debugRaycast = false, debugParticles = false, debugGamepads = false, debugTakeScreenshot;
|
|
48
48
|
|
|
49
49
|
///////////////////////////////////////////////////////////////////////////////
|
|
50
50
|
// Debug helper functions
|
package/src/engineDraw.js
CHANGED
|
@@ -217,8 +217,9 @@ class TextureInfo
|
|
|
217
217
|
* Create a TextureInfo, called automatically by the engine
|
|
218
218
|
* @param {HTMLImageElement|OffscreenCanvas} image
|
|
219
219
|
* @param {boolean} [useWebGL] - Should use WebGL if available?
|
|
220
|
+
* @param {boolean} [wrap] - Should the texture wrap (REPEAT) or clamp (CLAMP_TO_EDGE)?
|
|
220
221
|
*/
|
|
221
|
-
constructor(image, useWebGL=true)
|
|
222
|
+
constructor(image, useWebGL=true, wrap=false)
|
|
222
223
|
{
|
|
223
224
|
/** @property {HTMLImageElement|OffscreenCanvas} - image source */
|
|
224
225
|
this.image = image;
|
|
@@ -228,6 +229,8 @@ class TextureInfo
|
|
|
228
229
|
this.sizeInverse = image ? vec2(1/image.width, 1/image.height) : vec2();
|
|
229
230
|
/** @property {WebGLTexture} - WebGL texture */
|
|
230
231
|
this.glTexture = undefined;
|
|
232
|
+
/** @property {boolean} - true for REPEAT wrap mode, false for CLAMP_TO_EDGE */
|
|
233
|
+
this.wrap = wrap;
|
|
231
234
|
useWebGL && this.createWebGLTexture();
|
|
232
235
|
}
|
|
233
236
|
|
|
@@ -240,6 +243,14 @@ class TextureInfo
|
|
|
240
243
|
/** Check if the texture is webgl enabled
|
|
241
244
|
* @return {boolean} */
|
|
242
245
|
hasWebGL() { return !!this.glTexture; }
|
|
246
|
+
|
|
247
|
+
/** Set the wrap mode for this texture
|
|
248
|
+
* @param {boolean} [wrap] - true for REPEAT, false for CLAMP_TO_EDGE */
|
|
249
|
+
setWrap(wrap=true)
|
|
250
|
+
{
|
|
251
|
+
this.wrap = wrap;
|
|
252
|
+
glSetTextureWrap(this.glTexture, wrap);
|
|
253
|
+
}
|
|
243
254
|
}
|
|
244
255
|
|
|
245
256
|
///////////////////////////////////////////////////////////////////////////////
|
|
@@ -301,8 +312,13 @@ function drawTile(pos, size=vec2(1), tileInfo, color=WHITE,
|
|
|
301
312
|
}
|
|
302
313
|
else
|
|
303
314
|
{
|
|
304
|
-
// if no tile info, force untextured
|
|
305
|
-
|
|
315
|
+
// if no tile info, force untextured by zeroing rgba (so whatever
|
|
316
|
+
// texture is bound doesn't leak in) and folding color+additive
|
|
317
|
+
// into the additive slot — matches the Canvas2D path's
|
|
318
|
+
// color.add(additiveColor) on line ~337.
|
|
319
|
+
const combined = additiveColor ? color.add(additiveColor) : color;
|
|
320
|
+
glDraw(pos.x, pos.y, size.x, size.y, angle, 0, 0, 0, 0,
|
|
321
|
+
0, combined.rgbaInt());
|
|
306
322
|
}
|
|
307
323
|
}
|
|
308
324
|
else
|
|
@@ -441,6 +457,8 @@ function drawTextureWrapped(pos, size, wrapCount, texture=0, color=WHITE,
|
|
|
441
457
|
const textureInfo = typeof texture === 'number' ? textureInfos[texture] : texture;
|
|
442
458
|
ASSERT(textureInfo instanceof TextureInfo, 'texture not loaded');
|
|
443
459
|
ASSERT(textureInfo.size.x > 0, 'texture not loaded');
|
|
460
|
+
ASSERT(textureInfo.wrap,
|
|
461
|
+
'drawTextureWrapped requires a wrap-enabled texture; call textureInfo.setWrap(true) first');
|
|
444
462
|
|
|
445
463
|
if (useWebGL && glEnable)
|
|
446
464
|
{
|
package/src/engineExport.js
CHANGED
|
@@ -11,6 +11,7 @@ export
|
|
|
11
11
|
frameRate,
|
|
12
12
|
timeDelta,
|
|
13
13
|
engineObjects,
|
|
14
|
+
engineObjectsCollide,
|
|
14
15
|
frame,
|
|
15
16
|
time,
|
|
16
17
|
timeReal,
|
|
@@ -33,6 +34,7 @@ export
|
|
|
33
34
|
// Debug
|
|
34
35
|
ASSERT,
|
|
35
36
|
LOG,
|
|
37
|
+
debugPointSize,
|
|
36
38
|
debugRect,
|
|
37
39
|
debugPoly,
|
|
38
40
|
debugCircle,
|
|
@@ -77,11 +79,14 @@ export
|
|
|
77
79
|
gravity,
|
|
78
80
|
particleEmitRateScale,
|
|
79
81
|
glEnable,
|
|
82
|
+
glCircleSides,
|
|
80
83
|
gamepadsEnable,
|
|
81
84
|
gamepadDirectionEmulateStick,
|
|
82
85
|
inputWASDEmulateDirection,
|
|
86
|
+
touchInputEnable,
|
|
83
87
|
touchGamepadEnable,
|
|
84
88
|
touchGamepadCenterButtonSize,
|
|
89
|
+
touchGamepadButtonCount,
|
|
85
90
|
touchGamepadAnalog,
|
|
86
91
|
touchGamepadSize,
|
|
87
92
|
touchGamepadAlpha,
|
|
@@ -91,9 +96,6 @@ export
|
|
|
91
96
|
soundVolume,
|
|
92
97
|
soundDefaultRange,
|
|
93
98
|
soundDefaultTaper,
|
|
94
|
-
medalDisplayTime,
|
|
95
|
-
medalDisplaySlideTime,
|
|
96
|
-
medalDisplaySize,
|
|
97
99
|
|
|
98
100
|
// Setters for globals
|
|
99
101
|
setCameraPos,
|
|
@@ -125,6 +127,7 @@ export
|
|
|
125
127
|
setObjectMaxSpeed,
|
|
126
128
|
setGravity,
|
|
127
129
|
setParticleEmitRateScale,
|
|
130
|
+
setGLCircleSides,
|
|
128
131
|
setTouchInputEnable,
|
|
129
132
|
setGamepadsEnable,
|
|
130
133
|
setGamepadDirectionEmulateStick,
|
|
@@ -140,10 +143,6 @@ export
|
|
|
140
143
|
setSoundVolume,
|
|
141
144
|
setSoundDefaultRange,
|
|
142
145
|
setSoundDefaultTaper,
|
|
143
|
-
setMedalDisplayTime,
|
|
144
|
-
setMedalDisplaySlideTime,
|
|
145
|
-
setMedalDisplaySize,
|
|
146
|
-
setMedalsPreventUnlock,
|
|
147
146
|
setDebugWatermark,
|
|
148
147
|
setDebugKey,
|
|
149
148
|
|
|
@@ -173,6 +172,7 @@ export
|
|
|
173
172
|
percentLerp,
|
|
174
173
|
smoothStep,
|
|
175
174
|
nearestPowerOfTwo,
|
|
175
|
+
isPowerOfTwo,
|
|
176
176
|
isOverlapping,
|
|
177
177
|
isIntersecting,
|
|
178
178
|
lineTest,
|
|
@@ -252,6 +252,7 @@ export
|
|
|
252
252
|
drawLineList,
|
|
253
253
|
drawLine,
|
|
254
254
|
drawPoly,
|
|
255
|
+
drawRegularPoly,
|
|
255
256
|
drawEllipse,
|
|
256
257
|
drawCircle,
|
|
257
258
|
drawCanvas2D,
|
|
@@ -265,6 +266,7 @@ export
|
|
|
265
266
|
toggleFullscreen,
|
|
266
267
|
setCursor,
|
|
267
268
|
getCameraSize,
|
|
269
|
+
isOnScreen,
|
|
268
270
|
|
|
269
271
|
// WebGL
|
|
270
272
|
glCanvas,
|
|
@@ -272,6 +274,7 @@ export
|
|
|
272
274
|
glAntialias,
|
|
273
275
|
glClearCanvas,
|
|
274
276
|
glSetTexture,
|
|
277
|
+
glSetTextureWrap,
|
|
275
278
|
glCompileShader,
|
|
276
279
|
glCreateProgram,
|
|
277
280
|
glCreateTexture,
|
|
@@ -313,6 +316,7 @@ export
|
|
|
313
316
|
gamepadWasPressed,
|
|
314
317
|
gamepadWasReleased,
|
|
315
318
|
gamepadStick,
|
|
319
|
+
gamepadStickCount,
|
|
316
320
|
gamepadDpad,
|
|
317
321
|
gamepadConnected,
|
|
318
322
|
gamepadVibrate,
|
|
@@ -327,6 +331,7 @@ export
|
|
|
327
331
|
audioContext,
|
|
328
332
|
audioMasterGain,
|
|
329
333
|
audioDefaultSampleRate,
|
|
334
|
+
audioIsRunning,
|
|
330
335
|
Sound,
|
|
331
336
|
SoundInstance,
|
|
332
337
|
speak,
|
|
@@ -354,9 +359,4 @@ export
|
|
|
354
359
|
ParticleEmitter,
|
|
355
360
|
Particle,
|
|
356
361
|
|
|
357
|
-
// Medals
|
|
358
|
-
medals,
|
|
359
|
-
medalsPreventUnlock,
|
|
360
|
-
medalsInit,
|
|
361
|
-
Medal,
|
|
362
362
|
}
|
package/src/engineInput.js
CHANGED
|
@@ -66,7 +66,7 @@ const isTouchDevice = !headlessMode && window.ontouchstart !== undefined;
|
|
|
66
66
|
* This is useful to disable for html menus so the browser can handle input normally
|
|
67
67
|
* @param {boolean} preventDefault
|
|
68
68
|
* @memberof Input */
|
|
69
|
-
function setInputPreventDefault(preventDefault) { inputPreventDefault = preventDefault; }
|
|
69
|
+
function setInputPreventDefault(preventDefault=true) { inputPreventDefault = preventDefault; }
|
|
70
70
|
|
|
71
71
|
/** Clears an input key state
|
|
72
72
|
* @param {string|number} key
|