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.
Files changed (177) hide show
  1. package/COPYRIGHT.txt +38 -0
  2. package/FAQ.md +633 -0
  3. package/README.md +19 -1
  4. package/dist/littlejs.d.ts +349 -54
  5. package/dist/littlejs.esm.js +1469 -448
  6. package/dist/littlejs.esm.min.js +1 -1
  7. package/dist/littlejs.js +1438 -436
  8. package/dist/littlejs.min.js +1 -1
  9. package/dist/littlejs.release.js +1438 -436
  10. package/package.json +8 -1
  11. package/plugins/box2d.js +32 -7
  12. package/{src/engineMedals.js → plugins/medalSystem.js} +248 -196
  13. package/plugins/pathFinder.js +758 -0
  14. package/plugins/pluginExport.js +19 -0
  15. package/plugins/tween.js +1 -1
  16. package/plugins/tweenSystem.js +509 -0
  17. package/plugins/uiSystem.js +150 -11
  18. package/src/engine.js +2 -141
  19. package/src/engineAudio.js +8 -5
  20. package/src/engineBuild.mjs +4 -2
  21. package/src/engineDebug.js +1 -1
  22. package/src/engineDraw.js +21 -3
  23. package/src/engineExport.js +12 -12
  24. package/src/engineInput.js +1 -1
  25. package/src/engineLogo.js +146 -0
  26. package/src/engineMath.js +10 -3
  27. package/src/engineObject.js +11 -5
  28. package/src/engineParticles.js +6 -3
  29. package/src/engineRelease.js +1 -1
  30. package/src/engineSettings.js +0 -47
  31. package/src/engineUtilities.js +7 -2
  32. package/src/engineWebGL.js +34 -7
  33. package/src/jsconfig.json +3 -1
  34. package/.github/workflows/test.yml +0 -17
  35. package/AI.md +0 -173
  36. package/CLAUDE.md +0 -1
  37. package/examples/box2d/game.js +0 -192
  38. package/examples/box2d/gameObjects.js +0 -564
  39. package/examples/box2d/index.html +0 -9
  40. package/examples/box2d/scenes.js +0 -194
  41. package/examples/box2d/tiles.png +0 -0
  42. package/examples/breakout/game.js +0 -175
  43. package/examples/breakout/gameObjects.js +0 -147
  44. package/examples/breakout/index.html +0 -8
  45. package/examples/breakout/tiles.png +0 -0
  46. package/examples/breakoutTutorial/README.md +0 -521
  47. package/examples/breakoutTutorial/game.js +0 -191
  48. package/examples/breakoutTutorial/images/1.png +0 -0
  49. package/examples/breakoutTutorial/images/10.png +0 -0
  50. package/examples/breakoutTutorial/images/11.png +0 -0
  51. package/examples/breakoutTutorial/images/2.png +0 -0
  52. package/examples/breakoutTutorial/images/3.png +0 -0
  53. package/examples/breakoutTutorial/images/4.png +0 -0
  54. package/examples/breakoutTutorial/images/5.png +0 -0
  55. package/examples/breakoutTutorial/images/6.png +0 -0
  56. package/examples/breakoutTutorial/images/7.png +0 -0
  57. package/examples/breakoutTutorial/images/8.png +0 -0
  58. package/examples/breakoutTutorial/images/9.png +0 -0
  59. package/examples/breakoutTutorial/index.html +0 -8
  60. package/examples/electron/build.mjs +0 -127
  61. package/examples/electron/electron.js +0 -35
  62. package/examples/electron/game.js +0 -54
  63. package/examples/electron/index.html +0 -13
  64. package/examples/electron/package.json +0 -5
  65. package/examples/electron/tiles.png +0 -0
  66. package/examples/empty/game.js +0 -51
  67. package/examples/empty/index.html +0 -5
  68. package/examples/empty/tiles.png +0 -0
  69. package/examples/favicon.png +0 -0
  70. package/examples/games.jpg +0 -0
  71. package/examples/htmlMenu/game.js +0 -82
  72. package/examples/htmlMenu/index.html +0 -45
  73. package/examples/htmlMenu/tiles.png +0 -0
  74. package/examples/index.html +0 -62
  75. package/examples/logo.png +0 -0
  76. package/examples/logo2.png +0 -0
  77. package/examples/module/build.mjs +0 -124
  78. package/examples/module/game.js +0 -132
  79. package/examples/module/index.html +0 -10
  80. package/examples/module/tiles.png +0 -0
  81. package/examples/particles/index.html +0 -426
  82. package/examples/particles/tiles.png +0 -0
  83. package/examples/platformer/data/gameLevelData.tmx +0 -143
  84. package/examples/platformer/data/gameLevelData.tsx +0 -4
  85. package/examples/platformer/game.js +0 -147
  86. package/examples/platformer/gameCharacter.js +0 -309
  87. package/examples/platformer/gameEffects.js +0 -278
  88. package/examples/platformer/gameLevel.js +0 -205
  89. package/examples/platformer/gameLevelData.json +0 -171
  90. package/examples/platformer/gameObjects.js +0 -378
  91. package/examples/platformer/gamePlayer.js +0 -35
  92. package/examples/platformer/index.html +0 -9
  93. package/examples/platformer/tiles.png +0 -0
  94. package/examples/platformer/tilesLevel.png +0 -0
  95. package/examples/puzzle/game.js +0 -331
  96. package/examples/puzzle/index.html +0 -8
  97. package/examples/puzzle/tiles.png +0 -0
  98. package/examples/screenshot.jpg +0 -0
  99. package/examples/shorts/animation.js +0 -18
  100. package/examples/shorts/base.html +0 -54
  101. package/examples/shorts/blending.js +0 -14
  102. package/examples/shorts/box2d.js +0 -51
  103. package/examples/shorts/box2dCar.js +0 -59
  104. package/examples/shorts/box2dPool.js +0 -107
  105. package/examples/shorts/box2dTileLayer.js +0 -48
  106. package/examples/shorts/cameraDrag.js +0 -22
  107. package/examples/shorts/clock.js +0 -21
  108. package/examples/shorts/colors.js +0 -25
  109. package/examples/shorts/debugDraw.js +0 -37
  110. package/examples/shorts/empty.js +0 -30
  111. package/examples/shorts/flappyGame.js +0 -55
  112. package/examples/shorts/fontImage.js +0 -17
  113. package/examples/shorts/fps.js +0 -90
  114. package/examples/shorts/helloWorld.js +0 -11
  115. package/examples/shorts/hillGlideGame.js +0 -63
  116. package/examples/shorts/input.js +0 -64
  117. package/examples/shorts/landerGame.js +0 -57
  118. package/examples/shorts/maze.js +0 -48
  119. package/examples/shorts/medals.js +0 -51
  120. package/examples/shorts/music.js +0 -78
  121. package/examples/shorts/musicPlayer.js +0 -137
  122. package/examples/shorts/nineSlice.js +0 -40
  123. package/examples/shorts/parallax.js +0 -72
  124. package/examples/shorts/particles.js +0 -28
  125. package/examples/shorts/piano.js +0 -44
  126. package/examples/shorts/platformer.js +0 -45
  127. package/examples/shorts/pongGame.js +0 -41
  128. package/examples/shorts/postProcess.js +0 -62
  129. package/examples/shorts/sequencer.js +0 -124
  130. package/examples/shorts/shader.js +0 -29
  131. package/examples/shorts/shapes.js +0 -21
  132. package/examples/shorts/slidingPuzzle.js +0 -52
  133. package/examples/shorts/song.mp3 +0 -0
  134. package/examples/shorts/sound.js +0 -36
  135. package/examples/shorts/spaceGame.js +0 -58
  136. package/examples/shorts/spriteAtlas.js +0 -31
  137. package/examples/shorts/starfield.js +0 -15
  138. package/examples/shorts/texture.js +0 -16
  139. package/examples/shorts/textureWrapped.js +0 -12
  140. package/examples/shorts/tileLayer.js +0 -48
  141. package/examples/shorts/tileRaycast.js +0 -39
  142. package/examples/shorts/tiles.png +0 -0
  143. package/examples/shorts/tiltedView.js +0 -63
  144. package/examples/shorts/timers.js +0 -53
  145. package/examples/shorts/topDown.js +0 -38
  146. package/examples/shorts/tween.js +0 -24
  147. package/examples/shorts/uiSystem.js +0 -61
  148. package/examples/shorts/video.webm +0 -0
  149. package/examples/shorts/videoPlayer.js +0 -34
  150. package/examples/shorts.js +0 -743
  151. package/examples/starter/build.bat +0 -7
  152. package/examples/starter/build.mjs +0 -126
  153. package/examples/starter/game.js +0 -137
  154. package/examples/starter/index.html +0 -35
  155. package/examples/starter/tiles.png +0 -0
  156. package/examples/stress/index.html +0 -173
  157. package/examples/style.css +0 -150
  158. package/examples/tweenSystem/game.js +0 -171
  159. package/examples/tweenSystem/index.html +0 -10
  160. package/examples/tweenSystem/tiles.png +0 -0
  161. package/examples/typescript/build.mjs +0 -60
  162. package/examples/typescript/game.js +0 -100
  163. package/examples/typescript/game.ts +0 -132
  164. package/examples/typescript/index.html +0 -10
  165. package/examples/typescript/tiles.png +0 -0
  166. package/examples/typescript/tsconfig.json +0 -17
  167. package/examples/uiSystem/game.js +0 -139
  168. package/examples/uiSystem/index.html +0 -10
  169. package/examples/uiSystem/tiles.png +0 -0
  170. package/jsconfig.json +0 -12
  171. package/plugins/desktop.ini +0 -2
  172. package/reference.md +0 -448
  173. package/test/math.test.mjs +0 -774
  174. package/test/setup.mjs +0 -22
  175. package/test/smoke.test.mjs +0 -274
  176. package/test/tween.test.mjs +0 -576
  177. package/test/util.test.mjs +0 -80
package/test/setup.mjs DELETED
@@ -1,22 +0,0 @@
1
- // Minimal globalThis stubs so dist/littlejs.esm.js can import under Node.
2
- // The bundle has two top-level side effects that need host objects:
3
- // 1. `const isTouchDevice = !headlessMode && window.ontouchstart !== undefined;`
4
- // 2. `let audioContext = new AudioContext;`
5
- // Anything beyond these is on-demand inside functions we don't invoke from tests.
6
-
7
- globalThis.window = {};
8
-
9
- globalThis.AudioContext = class AudioContext
10
- {
11
- constructor() { this.currentTime = 0; this.destination = {}; this.state = 'running'; }
12
- createGain() { return { connect(){}, gain: { value: 0 } }; }
13
- createBuffer() { return {}; }
14
- createBufferSource() { return { connect(){}, start(){}, stop(){} }; }
15
- resume() { return Promise.resolve(); }
16
- };
17
-
18
- // Enable headless mode on the shared bundle instance. ES module caching
19
- // means every test file that imports the bundle gets this same instance,
20
- // so tile() / audio paths / input setup all take their headless branches.
21
- const { setHeadlessMode } = await import('../dist/littlejs.esm.js');
22
- setHeadlessMode(true);
@@ -1,274 +0,0 @@
1
- import { test } from 'node:test';
2
- import assert from 'node:assert/strict';
3
- import {
4
- EngineObject, ParticleEmitter, TileLayerData, TileInfo,
5
- TileLayer, CanvasLayer, Medal,
6
- Timer, tile, vec2, rgb,
7
- } from '../dist/littlejs.esm.js';
8
-
9
- const near = (a, b, eps=1e-9) => Math.abs(a - b) <= eps;
10
-
11
- // Tier 2-lite: verify core engine primitives can be constructed without
12
- // crashing or tripping ASSERTs (debug bundle keeps them live). We do NOT
13
- // boot the engine via engineInit and we do NOT call render() (which would
14
- // require a canvas context). update() on the base EngineObject is safe
15
- // because the default is a no-op.
16
-
17
- test('bundle exposes core engine symbols', () =>
18
- {
19
- // If the bundle failed to load under our stubs, this file wouldn't
20
- // have imported at all — so reaching here proves import succeeded.
21
- // These checks also guard against an export regression stripping
22
- // core names from the public surface.
23
- assert.equal(typeof EngineObject, 'function');
24
- assert.equal(typeof Timer, 'function');
25
- assert.equal(typeof tile, 'function');
26
- assert.equal(typeof vec2, 'function');
27
- });
28
-
29
- test('EngineObject constructs and base update is safe', () =>
30
- {
31
- const o = new EngineObject(vec2(0, 0), vec2(1, 1));
32
- assert.equal(o.pos.x, 0);
33
- assert.equal(o.pos.y, 0);
34
- assert.equal(o.size.x, 1);
35
- assert.equal(o.size.y, 1);
36
- // base class update is an empty method — calling it should not throw
37
- assert.doesNotThrow(() => o.update());
38
- });
39
-
40
- test('EngineObject with color and angle constructs', () =>
41
- {
42
- const o = new EngineObject(vec2(5, -3), vec2(2, 2), undefined, Math.PI/4, rgb(1, 0, 0));
43
- assert.equal(o.pos.x, 5);
44
- assert.equal(o.angle, Math.PI/4);
45
- });
46
-
47
- test('tile() returns a TileInfo', () =>
48
- {
49
- const t = tile(0, 16);
50
- assert(t instanceof TileInfo);
51
- });
52
-
53
- test('ParticleEmitter constructs with defaults', () =>
54
- {
55
- // minimum reasonable args: pos, angle, emitSize, emitTime, emitRate
56
- const e = new ParticleEmitter(vec2(0, 0), 0, 0, 0, 0);
57
- assert(e instanceof ParticleEmitter);
58
- assert(e instanceof EngineObject);
59
- });
60
-
61
- test('TileLayerData constructs', () =>
62
- {
63
- // TileLayerData(tile, direction, mirror, color) — minimal form
64
- assert.doesNotThrow(() => new TileLayerData(0));
65
- assert.doesNotThrow(() => new TileLayerData(5, 1, false));
66
- });
67
-
68
- test('Timer lifecycle (unset -> set -> elapsed check)', () =>
69
- {
70
- const t = new Timer();
71
- assert.equal(t.isSet(), false);
72
- assert.equal(t.get(), 0); // returns 0 when unset
73
- assert.equal(t.getSetTime(), 0);
74
- t.set(1);
75
- assert.equal(t.isSet(), true);
76
- assert.equal(t.getSetTime(), 1);
77
- t.unset();
78
- assert.equal(t.isSet(), false);
79
- });
80
-
81
- test('Timer constructed with duration is set', () =>
82
- {
83
- const t = new Timer(5);
84
- assert.equal(t.isSet(), true);
85
- assert.equal(t.getSetTime(), 5);
86
- });
87
-
88
- // The engine's `time` global stays at 0 because the main loop never runs
89
- // in headless mode without engineInit. That lets us check time-derived
90
- // Timer methods against known reference points.
91
-
92
- test('Timer active / elapsed / get for a fresh positive-duration timer', () =>
93
- {
94
- const t = new Timer(1);
95
- assert.equal(t.active(), true); // time(0) < setAt(1)
96
- assert.equal(t.elapsed(), false);
97
- assert(near(t.get(), -1)); // negative = still active
98
- });
99
-
100
- test('Timer active / elapsed / get for an already-elapsed timer', () =>
101
- {
102
- // negative duration -> timer's internal target is in the past
103
- const t = new Timer(-2);
104
- assert.equal(t.active(), false);
105
- assert.equal(t.elapsed(), true);
106
- assert(near(t.get(), 2)); // positive = how long since elapsed
107
- });
108
-
109
- test('Timer with zero duration is immediately elapsed', () =>
110
- {
111
- const t = new Timer(0);
112
- assert.equal(t.active(), false);
113
- assert.equal(t.elapsed(), true);
114
- assert(near(t.get(), 0));
115
- });
116
-
117
- test('Timer getPercent for fresh positive-duration timer is 0', () =>
118
- {
119
- // at time=0 with a timer set for duration 1, no time has elapsed yet
120
- assert(near(new Timer(1).getPercent(), 0));
121
- });
122
-
123
- test('Timer unset returns 0 from get/getPercent/getSetTime', () =>
124
- {
125
- const t = new Timer();
126
- assert.equal(t.isSet(), false);
127
- assert.equal(t.get(), 0);
128
- assert.equal(t.getPercent(), 0);
129
- assert.equal(t.getSetTime(), 0);
130
- });
131
-
132
- ///////////////////////////////////////////////////////////////////////////////
133
- // EngineObject methods
134
-
135
- test('EngineObject.setCollision applies each flag to the right slot', () =>
136
- {
137
- // Asymmetric patterns: each of the four (collideSolidObjects, isSolid,
138
- // collideTiles, collideRaycast) slots gets a different value from its
139
- // neighbors, so any pairwise swap of assignments would show up.
140
- const o = new EngineObject(vec2(0, 0), vec2(1, 1));
141
- // pattern A: [T, F, T, F]
142
- o.setCollision(true, false, true, false);
143
- assert.equal(o.collideSolidObjects, true);
144
- assert.equal(o.isSolid, false);
145
- assert.equal(o.collideTiles, true);
146
- assert.equal(o.collideRaycast, false);
147
- // pattern B: [T, T, F, T] — flips everything from A (ASSERT requires collideSolidObjects||!isSolid)
148
- o.setCollision(true, true, false, true);
149
- assert.equal(o.collideSolidObjects, true);
150
- assert.equal(o.isSolid, true);
151
- assert.equal(o.collideTiles, false);
152
- assert.equal(o.collideRaycast, true);
153
- // defaults: all true
154
- o.setCollision();
155
- assert.equal(o.collideSolidObjects, true);
156
- assert.equal(o.isSolid, true);
157
- assert.equal(o.collideTiles, true);
158
- assert.equal(o.collideRaycast, true);
159
- });
160
-
161
- test('EngineObject.addChild / removeChild maintain bidirectional links', () =>
162
- {
163
- const parent = new EngineObject(vec2(0, 0), vec2(1, 1));
164
- const child = new EngineObject(vec2(0, 0), vec2(1, 1));
165
- const localPos = vec2(2, 3);
166
- parent.addChild(child, localPos, 0.5);
167
- assert.equal(child.parent, parent);
168
- assert.deepEqual(parent.children, [child]);
169
- assert.equal(child.localAngle, 0.5);
170
- // localPos is stored as a copy — mutating the original doesn't affect the child
171
- assert.equal(child.localPos.x, 2);
172
- assert.equal(child.localPos.y, 3);
173
- localPos.x = 999;
174
- assert.equal(child.localPos.x, 2);
175
- // removeChild clears parent and splices children
176
- parent.removeChild(child);
177
- assert.equal(child.parent, undefined);
178
- assert.deepEqual(parent.children, []);
179
- });
180
-
181
- test('EngineObject.destroy is idempotent and cascades to children', () =>
182
- {
183
- const parent = new EngineObject(vec2(0, 0), vec2(1, 1));
184
- const childA = new EngineObject(vec2(0, 0), vec2(1, 1));
185
- const childB = new EngineObject(vec2(0, 0), vec2(1, 1));
186
- parent.addChild(childA);
187
- parent.addChild(childB);
188
- parent.destroy();
189
- assert.equal(parent.destroyed, true);
190
- // children cascade-destroyed
191
- assert.equal(childA.destroyed, true);
192
- assert.equal(childB.destroyed, true);
193
- // children have parent cleared
194
- assert.equal(childA.parent, undefined);
195
- assert.equal(childB.parent, undefined);
196
- // idempotent — second call is a no-op (no throw)
197
- assert.doesNotThrow(() => parent.destroy());
198
- });
199
-
200
- test('EngineObject.destroy disconnects from parent', () =>
201
- {
202
- const parent = new EngineObject(vec2(0, 0), vec2(1, 1));
203
- const child = new EngineObject(vec2(0, 0), vec2(1, 1));
204
- parent.addChild(child);
205
- child.destroy();
206
- assert.equal(child.destroyed, true);
207
- assert.equal(child.parent, undefined);
208
- assert.deepEqual(parent.children, []); // parent's children array spliced
209
- });
210
-
211
- test('EngineObject.getAliveTime is 0 right after construction (time=0 in headless)', () =>
212
- {
213
- const o = new EngineObject(vec2(0, 0), vec2(1, 1));
214
- assert(near(o.getAliveTime(), 0));
215
- });
216
-
217
- ///////////////////////////////////////////////////////////////////////////////
218
- // Medal
219
-
220
- test('Medal construction sets fields and registers in medals[]', () =>
221
- {
222
- const m = new Medal(100, 'Test Medal', 'A test medal', '🏆');
223
- assert.equal(m.id, 100);
224
- assert.equal(m.name, 'Test Medal');
225
- assert.equal(m.description, 'A test medal');
226
- assert.equal(m.icon, '🏆');
227
- assert.equal(m.unlocked, false);
228
- });
229
-
230
- test('Medal description and icon default when omitted', () =>
231
- {
232
- const m = new Medal(101, 'Defaults Medal');
233
- assert.equal(m.description, '');
234
- assert.equal(m.icon, '🏆'); // default trophy
235
- assert.equal(m.unlocked, false);
236
- });
237
-
238
- ///////////////////////////////////////////////////////////////////////////////
239
- // CanvasLayer / TileLayer
240
-
241
- test('CanvasLayer extends EngineObject and has no canvas in headless', () =>
242
- {
243
- const cl = new CanvasLayer(vec2(0, 0), vec2(10, 10), 0, 0, vec2(64), false);
244
- assert(cl instanceof EngineObject);
245
- assert(cl instanceof CanvasLayer);
246
- assert.equal(cl.canvas, undefined); // headless: no OffscreenCanvas created
247
- assert.equal(cl.mass, 0); // physics disabled by default
248
- });
249
-
250
- test('TileLayer extends CanvasLayer and stubs render methods in headless', () =>
251
- {
252
- const tl = new TileLayer(vec2(0, 0), vec2(4, 3), tile(0, 16), 0, false);
253
- assert(tl instanceof CanvasLayer);
254
- assert(tl instanceof TileLayer);
255
- // in headless the render-family methods are replaced with no-op arrow functions
256
- assert.doesNotThrow(() => tl.render());
257
- assert.doesNotThrow(() => tl.redraw());
258
- });
259
-
260
- test('drawTextureWrapped is exported', async () =>
261
- {
262
- const mod = await import('../dist/littlejs.esm.js');
263
- assert.equal(typeof mod.drawTextureWrapped, 'function');
264
- });
265
-
266
- test('drawTextureWrapped is callable in headless mode', async () =>
267
- {
268
- const mod = await import('../dist/littlejs.esm.js');
269
- const { drawTextureWrapped, vec2 } = mod;
270
- // headlessMode short-circuits before texture lookup, so this just
271
- // exercises argument-type ASSERTs (which are stripped in release)
272
- assert.doesNotThrow(() =>
273
- drawTextureWrapped(vec2(), vec2(1, 1), vec2(2, 2)));
274
- });