littlejsengine 1.18.4 → 1.18.8
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 +354 -55
- package/dist/littlejs.esm.js +1531 -489
- package/dist/littlejs.esm.min.js +1 -1
- package/dist/littlejs.js +1499 -476
- package/dist/littlejs.min.js +1 -1
- package/dist/littlejs.release.js +1493 -470
- 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 → tweenSystem.js} +7 -7
- package/plugins/uiSystem.js +186 -25
- package/src/engine.js +2 -141
- package/src/engineAudio.js +3 -3
- package/src/engineBuild.mjs +4 -2
- package/src/engineDebug.js +7 -7
- package/src/engineDraw.js +26 -8
- package/src/engineExport.js +13 -13
- package/src/engineInput.js +8 -8
- package/src/engineLogo.js +146 -0
- package/src/engineMath.js +15 -6
- 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 +13 -8
- 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
|
@@ -1,171 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
LittleJS Tween System Example
|
|
3
|
-
- Demonstrates every public feature of the tween plugin
|
|
4
|
-
- 1) Property tween, 2) Callback tween, 3) Chaining, 4) Loop+PingPong,
|
|
5
|
-
5) Easing showcase, 6) Real-time tween that survives pause
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
'use strict';
|
|
9
|
-
|
|
10
|
-
import * as LJS from '../../dist/littlejs.esm.js';
|
|
11
|
-
const { vec2, hsl, drawRect, drawText } = LJS;
|
|
12
|
-
const { Tween, tweenProperty, Ease } = LJS;
|
|
13
|
-
|
|
14
|
-
LJS.setCanvasFixedSize(vec2(1920, 1080));
|
|
15
|
-
LJS.setCanvasPixelated(false);
|
|
16
|
-
|
|
17
|
-
///////////////////////////////////////////////////////////////////////////////
|
|
18
|
-
// Layout — world-space row positions
|
|
19
|
-
|
|
20
|
-
const ROW_PROPERTY = 9;
|
|
21
|
-
const ROW_COUNTDOWN = 7.2;
|
|
22
|
-
const ROW_CHAIN = 5.4;
|
|
23
|
-
const ROW_LOOP = 3.6;
|
|
24
|
-
const ROW_PINGPONG = 1.8;
|
|
25
|
-
const ROW_EASE_TOP = 0;
|
|
26
|
-
const EASE_SPACING = 1.2;
|
|
27
|
-
const ROW_REALTIME = -10.0;
|
|
28
|
-
const ROW_VECTOR2 = -11.7;
|
|
29
|
-
const ROW_COLOR = -13.2;
|
|
30
|
-
const LABEL_X = -9;
|
|
31
|
-
|
|
32
|
-
///////////////////////////////////////////////////////////////////////////////
|
|
33
|
-
// State
|
|
34
|
-
|
|
35
|
-
const moverObj = { pos: vec2(-5, ROW_PROPERTY) };
|
|
36
|
-
const chainObj = { pos: vec2(-5, ROW_CHAIN), size: 1 };
|
|
37
|
-
const loopObj = { pos: vec2(-5, ROW_LOOP) };
|
|
38
|
-
const pingPongObj = { pos: vec2(-5, ROW_PINGPONG) };
|
|
39
|
-
const realObj = { pos: vec2(-5, ROW_REALTIME) };
|
|
40
|
-
const vec2Obj = { pos: vec2(-5, ROW_VECTOR2 + 0.4) };
|
|
41
|
-
const colorObj = { pos: vec2(0, ROW_COLOR), color: hsl(0, 0.8, 0.6) };
|
|
42
|
-
let countdownValue = 10;
|
|
43
|
-
|
|
44
|
-
const easingDemos =
|
|
45
|
-
[
|
|
46
|
-
['LINEAR', Ease.LINEAR],
|
|
47
|
-
['OUT(SINE)', Ease.OUT(Ease.SINE)],
|
|
48
|
-
['OUT(POWER(2))', Ease.OUT(Ease.POWER(2))],
|
|
49
|
-
['OUT(BACK)', Ease.OUT(Ease.BACK)],
|
|
50
|
-
['OUT(ELASTIC)', Ease.OUT(Ease.ELASTIC)],
|
|
51
|
-
['OUT(BOUNCE)', Ease.OUT(Ease.BOUNCE)],
|
|
52
|
-
['IN_OUT(POW(3))', Ease.IN_OUT(Ease.POWER(3))],
|
|
53
|
-
['BEZIER', Ease.BEZIER(0.25, 0.1, 0.25, 1)],
|
|
54
|
-
];
|
|
55
|
-
const easingObjs = easingDemos.map((_, i) => ({
|
|
56
|
-
pos: vec2(-5, ROW_EASE_TOP - i * EASE_SPACING)
|
|
57
|
-
}));
|
|
58
|
-
|
|
59
|
-
///////////////////////////////////////////////////////////////////////////////
|
|
60
|
-
function gameInit()
|
|
61
|
-
{
|
|
62
|
-
LJS.setCanvasClearColor(hsl(0, 0, 0.1));
|
|
63
|
-
|
|
64
|
-
// 1) Basic property tween, ping-pong forever
|
|
65
|
-
tweenProperty(moverObj.pos, 'x', -5, 5, 2).pingPong();
|
|
66
|
-
|
|
67
|
-
// 2) Callback tween — countdown 10 → 0 looping forever
|
|
68
|
-
new Tween((v) => { countdownValue = v; }, 10, 0, 5).loop();
|
|
69
|
-
|
|
70
|
-
// 3) Chaining: slide right, then shrink, then restart the chain
|
|
71
|
-
startChain();
|
|
72
|
-
|
|
73
|
-
// 4) Loop forever and PingPong forever on adjacent rows
|
|
74
|
-
tweenProperty(loopObj.pos, 'x', -5, 5, 1.5).loop();
|
|
75
|
-
tweenProperty(pingPongObj.pos, 'x', -5, 5, 1.5).pingPong();
|
|
76
|
-
|
|
77
|
-
// 5) Easing showcase — each row uses a different curve, all ping-pong
|
|
78
|
-
for (let i = 0; i < easingDemos.length; i++)
|
|
79
|
-
{
|
|
80
|
-
const [, easeFn] = easingDemos[i];
|
|
81
|
-
tweenProperty(easingObjs[i].pos, 'x', -5, 5, 2)
|
|
82
|
-
.setEase(easeFn)
|
|
83
|
-
.pingPong();
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
// 6) Real-time tween — keeps moving while game is paused
|
|
87
|
-
tweenProperty(realObj.pos, 'x', -5, 5, 2, { useRealTime: true }).pingPong();
|
|
88
|
-
|
|
89
|
-
// 7) Vector2 tween — moves diagonally (interpolates both x and y)
|
|
90
|
-
tweenProperty(vec2Obj, 'pos',
|
|
91
|
-
vec2(-5, ROW_VECTOR2 + 0.4),
|
|
92
|
-
vec2( 5, ROW_VECTOR2 - 0.4),
|
|
93
|
-
2).pingPong();
|
|
94
|
-
|
|
95
|
-
// 8) Color tween — pulses between two colors using Color.lerp()
|
|
96
|
-
tweenProperty(colorObj, 'color', hsl(0, 0.8, 0.6), hsl(0.6, 0.8, 0.6), 2).pingPong();
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
function startChain()
|
|
100
|
-
{
|
|
101
|
-
chainObj.pos.x = -5;
|
|
102
|
-
chainObj.size = 1;
|
|
103
|
-
new Tween((v) => { chainObj.pos.x = v; }, -5, 5, 1.5)
|
|
104
|
-
.then(() =>
|
|
105
|
-
{
|
|
106
|
-
new Tween((v) => { chainObj.size = v; }, 1, 0.2, 0.8)
|
|
107
|
-
.then(startChain);
|
|
108
|
-
});
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
///////////////////////////////////////////////////////////////////////////////
|
|
112
|
-
function gameUpdate() { }
|
|
113
|
-
|
|
114
|
-
///////////////////////////////////////////////////////////////////////////////
|
|
115
|
-
function gameUpdatePost()
|
|
116
|
-
{
|
|
117
|
-
// Press P to toggle pause; the real-time row keeps moving regardless.
|
|
118
|
-
if (LJS.keyWasPressed('KeyP')) LJS.setPaused(!LJS.getPaused());
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
///////////////////////////////////////////////////////////////////////////////
|
|
122
|
-
function gameRender()
|
|
123
|
-
{
|
|
124
|
-
drawRect(moverObj.pos, vec2(0.8), hsl(0.60, 0.8, 0.6));
|
|
125
|
-
drawRect(chainObj.pos, vec2(chainObj.size), hsl(0.05, 0.8, 0.6));
|
|
126
|
-
drawRect(loopObj.pos, vec2(0.8), hsl(0.30, 0.8, 0.6));
|
|
127
|
-
drawRect(pingPongObj.pos, vec2(0.8), hsl(0.50, 0.8, 0.6));
|
|
128
|
-
for (let i = 0; i < easingObjs.length; i++)
|
|
129
|
-
drawRect(easingObjs[i].pos, vec2(0.6), hsl(i / easingObjs.length, 0.8, 0.6));
|
|
130
|
-
drawRect(realObj.pos, vec2(0.8), hsl(0.85, 0.8, 0.6));
|
|
131
|
-
drawRect(vec2Obj.pos, vec2(0.8), hsl(0.15, 0.8, 0.6));
|
|
132
|
-
drawRect(colorObj.pos, vec2(0.8), colorObj.color);
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
///////////////////////////////////////////////////////////////////////////////
|
|
136
|
-
function gameRenderPost()
|
|
137
|
-
{
|
|
138
|
-
const labelColor = hsl(0, 0, 1);
|
|
139
|
-
const subColor = hsl(0, 0, 0.75);
|
|
140
|
-
|
|
141
|
-
// Title and subtitle (world space, centered)
|
|
142
|
-
drawText('LittleJS Tween System Example', vec2(0, 13), 1.4, labelColor);
|
|
143
|
-
drawText('Press P to pause — the bottom row uses useRealTime and keeps moving',
|
|
144
|
-
vec2(0, 11.5), 0.5, subColor);
|
|
145
|
-
|
|
146
|
-
// Row labels (centered at LABEL_X, sitting just left of the animating objects)
|
|
147
|
-
drawText('property', vec2(LABEL_X, ROW_PROPERTY), 0.6, labelColor);
|
|
148
|
-
drawText('callback', vec2(LABEL_X, ROW_COUNTDOWN), 0.6, labelColor);
|
|
149
|
-
drawText('chain', vec2(LABEL_X, ROW_CHAIN), 0.6, labelColor);
|
|
150
|
-
drawText('loop()', vec2(LABEL_X, ROW_LOOP), 0.6, labelColor);
|
|
151
|
-
drawText('pingPong()', vec2(LABEL_X, ROW_PINGPONG), 0.6, labelColor);
|
|
152
|
-
for (let i = 0; i < easingDemos.length; i++)
|
|
153
|
-
{
|
|
154
|
-
const [label] = easingDemos[i];
|
|
155
|
-
drawText(label, vec2(LABEL_X, ROW_EASE_TOP - i * EASE_SPACING), 0.5, labelColor);
|
|
156
|
-
}
|
|
157
|
-
drawText('useRealTime', vec2(LABEL_X, ROW_REALTIME), 0.6, labelColor);
|
|
158
|
-
drawText('Vector2', vec2(LABEL_X, ROW_VECTOR2), 0.6, labelColor);
|
|
159
|
-
drawText('Color', vec2(LABEL_X, ROW_COLOR), 0.6, labelColor);
|
|
160
|
-
|
|
161
|
-
// Live countdown value sitting on the callback row, between label and the easing rows
|
|
162
|
-
const countdownText = Math.ceil(countdownValue).toString();
|
|
163
|
-
drawText(countdownText, vec2(0, ROW_COUNTDOWN), 1.2, hsl(0, 0.8, 0.6));
|
|
164
|
-
|
|
165
|
-
// Pause indicator
|
|
166
|
-
if (LJS.getPaused())
|
|
167
|
-
drawText('PAUSED', vec2(0, -5), 2.5, hsl(0, 1, 0.7));
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
///////////////////////////////////////////////////////////////////////////////
|
|
171
|
-
LJS.engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRenderPost, ['tiles.png']);
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
<!DOCTYPE html><head>
|
|
2
|
-
<title>LittleJS Tween System Example</title>
|
|
3
|
-
<meta charset=utf-8>
|
|
4
|
-
<meta name=apple-mobile-web-app-capable content=yes>
|
|
5
|
-
<meta name=mobile-web-app-capable content=yes>
|
|
6
|
-
<link rel=icon type=image/png href=../favicon.png>
|
|
7
|
-
</head><body>
|
|
8
|
-
|
|
9
|
-
<!-- Add your game scripts here -->
|
|
10
|
-
<script src=game.js type=module></script>
|
|
Binary file
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* LittleJS TypeScript Example Build System
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
'use strict';
|
|
6
|
-
|
|
7
|
-
import { fileURLToPath } from 'node:url';
|
|
8
|
-
import { dirname, join } from 'node:path';
|
|
9
|
-
import fs from 'node:fs';
|
|
10
|
-
import { execSync } from 'node:child_process';
|
|
11
|
-
|
|
12
|
-
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
13
|
-
const PROGRAM_NAME = 'game';
|
|
14
|
-
const BUILD_FOLDER = join(__dirname, 'build');
|
|
15
|
-
|
|
16
|
-
// Define TypeScript source files
|
|
17
|
-
const tsSourceFiles = [
|
|
18
|
-
'game.ts',
|
|
19
|
-
// add your TypeScript files here
|
|
20
|
-
];
|
|
21
|
-
|
|
22
|
-
// Corresponding JS output files
|
|
23
|
-
const jsSourceFiles = tsSourceFiles.map(file => file.replace('.ts', '.js'));
|
|
24
|
-
|
|
25
|
-
console.log(`Building TypeScript for ${PROGRAM_NAME}...`);
|
|
26
|
-
const startTime = Date.now();
|
|
27
|
-
|
|
28
|
-
console.log(`Removing old build folder...`);
|
|
29
|
-
fs.rmSync(BUILD_FOLDER, { recursive: true, force: true });
|
|
30
|
-
|
|
31
|
-
console.log(`Compiling TypeScript...`);
|
|
32
|
-
|
|
33
|
-
// Use tsconfig.json for compilation settings
|
|
34
|
-
try
|
|
35
|
-
{
|
|
36
|
-
const result = execSync(`npx -p typescript tsc`, {cwd: __dirname, encoding: 'utf8', stdio: 'pipe'});
|
|
37
|
-
console.log(result);
|
|
38
|
-
} catch (error)
|
|
39
|
-
{
|
|
40
|
-
console.error('TypeScript compilation errors:');
|
|
41
|
-
if (error.stdout) console.log(error.stdout);
|
|
42
|
-
if (error.stderr) console.error(error.stderr);
|
|
43
|
-
console.error('TypeScript compilation failed!');
|
|
44
|
-
process.exit(1);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
console.log(`Copying js files back to root...`);
|
|
48
|
-
for (const file of jsSourceFiles)
|
|
49
|
-
{
|
|
50
|
-
// TypeScript outputs to build/examples/typescript/ because of relative paths
|
|
51
|
-
const buildFile = join(BUILD_FOLDER, 'examples', 'typescript', file);
|
|
52
|
-
const targetFile = join(__dirname, file);
|
|
53
|
-
console.log(`Copying ${file}...`);
|
|
54
|
-
if (fs.existsSync(buildFile))
|
|
55
|
-
fs.copyFileSync(buildFile, targetFile);
|
|
56
|
-
else
|
|
57
|
-
console.error(`✗ Build file not found: ${buildFile}`);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
console.log(`TypeScript built in ${((Date.now() - startTime)/1e3).toFixed(2)} seconds!`);
|
|
@@ -1,100 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
Little JS TypeScript Demo
|
|
3
|
-
- A simple starter project
|
|
4
|
-
- Shows how to use LittleJS with modules
|
|
5
|
-
*/
|
|
6
|
-
'use strict';
|
|
7
|
-
// import LittleJS module
|
|
8
|
-
import * as LJS from '../../dist/littlejs.esm.js';
|
|
9
|
-
const { tile, vec2, hsl } = LJS;
|
|
10
|
-
// show the LittleJS splash screen
|
|
11
|
-
LJS.setShowSplashScreen(true);
|
|
12
|
-
// fix texture bleeding by shrinking tile slightly
|
|
13
|
-
LJS.setTileDefaultBleed(.5);
|
|
14
|
-
// sound effects
|
|
15
|
-
const sound_click = new LJS.Sound([1, .5]);
|
|
16
|
-
// medals
|
|
17
|
-
const medal_example = new LJS.Medal(0, 'Example Medal', 'Welcome to LittleJS!');
|
|
18
|
-
LJS.medalsInit('Hello World');
|
|
19
|
-
// game variables
|
|
20
|
-
let particleEmitter;
|
|
21
|
-
///////////////////////////////////////////////////////////////////////////////
|
|
22
|
-
function gameInit() {
|
|
23
|
-
// create tile collision and visible tile layer
|
|
24
|
-
const pos = vec2();
|
|
25
|
-
const tileLayer = new LJS.TileCollisionLayer(pos, vec2(32, 16));
|
|
26
|
-
// get level data from the tiles image
|
|
27
|
-
const mainContext = LJS.mainContext;
|
|
28
|
-
const tileImage = LJS.textureInfos[0].image;
|
|
29
|
-
mainContext.drawImage(tileImage, 0, 0);
|
|
30
|
-
const imageData = mainContext.getImageData(0, 0, tileImage.width, tileImage.height).data;
|
|
31
|
-
for (pos.x = tileLayer.size.x; pos.x--;)
|
|
32
|
-
for (pos.y = tileLayer.size.y; pos.y--;) {
|
|
33
|
-
// check if this pixel is set
|
|
34
|
-
const i = pos.x + tileImage.width * (15 + tileLayer.size.y - pos.y);
|
|
35
|
-
if (!imageData[4 * i])
|
|
36
|
-
continue;
|
|
37
|
-
// set tile data
|
|
38
|
-
const tileIndex = 1;
|
|
39
|
-
const direction = LJS.randInt(4);
|
|
40
|
-
const mirror = !LJS.randInt(2);
|
|
41
|
-
const color = LJS.randColor();
|
|
42
|
-
const data = new LJS.TileLayerData(tileIndex, direction, mirror, color);
|
|
43
|
-
tileLayer.setData(pos, data);
|
|
44
|
-
tileLayer.setCollisionData(pos);
|
|
45
|
-
}
|
|
46
|
-
// draw tile layer with new data
|
|
47
|
-
tileLayer.redraw();
|
|
48
|
-
// move camera to center of collision
|
|
49
|
-
LJS.setCameraPos(tileLayer.size.scale(.5));
|
|
50
|
-
LJS.setCameraScale(32);
|
|
51
|
-
// enable gravity
|
|
52
|
-
LJS.setGravity(vec2(0, -.01));
|
|
53
|
-
// create particle emitter
|
|
54
|
-
particleEmitter = new LJS.ParticleEmitter(vec2(16, 9), 0, // emitPos, emitAngle
|
|
55
|
-
0, 0, 500, 3.14, // emitSize, emitTime, rate, cone
|
|
56
|
-
tile(0, 16), // tileIndex, tileSize
|
|
57
|
-
hsl(1, 1, 1), hsl(0, 0, 0), // colorStartA, colorStartB
|
|
58
|
-
hsl(0, 0, 0, 0), hsl(0, 0, 0, 0), // colorEndA, colorEndB
|
|
59
|
-
1, .2, .2, .1, .05, // time, sizeStart, sizeEnd, speed, angleSpeed
|
|
60
|
-
.99, 1, 1, 3.14, // damping, angleDamping, gravityScale, cone
|
|
61
|
-
.05, .5, true, true // fadeRate, randomness, collide, additive
|
|
62
|
-
);
|
|
63
|
-
particleEmitter.restitution = .3; // bounce when it collides
|
|
64
|
-
particleEmitter.trailScale = 2; // stretch stretch as it moves
|
|
65
|
-
particleEmitter.velocityInheritance = .3; // inherit emitter velocity
|
|
66
|
-
}
|
|
67
|
-
///////////////////////////////////////////////////////////////////////////////
|
|
68
|
-
function gameUpdate() {
|
|
69
|
-
if (LJS.mouseWasPressed(0)) {
|
|
70
|
-
// play sound when mouse is pressed
|
|
71
|
-
sound_click.play(LJS.mousePos);
|
|
72
|
-
// change particle color and set to fade out
|
|
73
|
-
particleEmitter.colorStartA = LJS.randColor();
|
|
74
|
-
particleEmitter.colorStartB = LJS.randColor();
|
|
75
|
-
particleEmitter.colorEndA = particleEmitter.colorStartA.scale(1, 0);
|
|
76
|
-
particleEmitter.colorEndB = particleEmitter.colorStartB.scale(1, 0);
|
|
77
|
-
// unlock medals
|
|
78
|
-
medal_example.unlock();
|
|
79
|
-
}
|
|
80
|
-
// move particles to mouse location if on screen
|
|
81
|
-
if (LJS.mousePosScreen.x)
|
|
82
|
-
particleEmitter.pos = LJS.mousePos;
|
|
83
|
-
}
|
|
84
|
-
///////////////////////////////////////////////////////////////////////////////
|
|
85
|
-
function gameUpdatePost() {
|
|
86
|
-
}
|
|
87
|
-
///////////////////////////////////////////////////////////////////////////////
|
|
88
|
-
function gameRender() {
|
|
89
|
-
// draw a grey square in the background
|
|
90
|
-
LJS.drawRect(vec2(16, 8), vec2(20, 14), hsl(0, 0, .6));
|
|
91
|
-
// draw the logo as a tile
|
|
92
|
-
LJS.drawTile(vec2(21, 5), vec2(4.5), tile(3, 128));
|
|
93
|
-
}
|
|
94
|
-
///////////////////////////////////////////////////////////////////////////////
|
|
95
|
-
function gameRenderPost() {
|
|
96
|
-
LJS.drawTextScreen('LittleJS with TypeScript', vec2(LJS.mainCanvasSize.x / 2, 80), 80);
|
|
97
|
-
}
|
|
98
|
-
///////////////////////////////////////////////////////////////////////////////
|
|
99
|
-
// Startup LittleJS Engine
|
|
100
|
-
LJS.engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRenderPost, ['tiles.png']);
|
|
@@ -1,132 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
Little JS TypeScript Demo
|
|
3
|
-
- A simple starter project
|
|
4
|
-
- Shows how to use LittleJS with modules
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
'use strict';
|
|
8
|
-
|
|
9
|
-
// import LittleJS module
|
|
10
|
-
import * as LJS from '../../dist/littlejs.esm.js';
|
|
11
|
-
const {tile, vec2, hsl} = LJS;
|
|
12
|
-
|
|
13
|
-
// show the LittleJS splash screen
|
|
14
|
-
LJS.setShowSplashScreen(true);
|
|
15
|
-
|
|
16
|
-
// fix texture bleeding by shrinking tile slightly
|
|
17
|
-
LJS.setTileDefaultBleed(.5);
|
|
18
|
-
|
|
19
|
-
// sound effects
|
|
20
|
-
const sound_click = new LJS.Sound([1,.5]);
|
|
21
|
-
|
|
22
|
-
// medals
|
|
23
|
-
const medal_example = new LJS.Medal(0, 'Example Medal', 'Welcome to LittleJS!');
|
|
24
|
-
LJS.medalsInit('Hello World');
|
|
25
|
-
|
|
26
|
-
// game variables
|
|
27
|
-
let particleEmitter;
|
|
28
|
-
|
|
29
|
-
///////////////////////////////////////////////////////////////////////////////
|
|
30
|
-
function gameInit()
|
|
31
|
-
{
|
|
32
|
-
// create tile collision and visible tile layer
|
|
33
|
-
const pos = vec2();
|
|
34
|
-
const tileLayer = new LJS.TileCollisionLayer(pos, vec2(32,16));
|
|
35
|
-
|
|
36
|
-
// get level data from the tiles image
|
|
37
|
-
const mainContext = LJS.mainContext;
|
|
38
|
-
const tileImage = LJS.textureInfos[0].image;
|
|
39
|
-
mainContext.drawImage(tileImage, 0, 0);
|
|
40
|
-
const imageData = mainContext.getImageData(0,0,tileImage.width,tileImage.height).data;
|
|
41
|
-
for (pos.x = tileLayer.size.x; pos.x--;)
|
|
42
|
-
for (pos.y = tileLayer.size.y; pos.y--;)
|
|
43
|
-
{
|
|
44
|
-
// check if this pixel is set
|
|
45
|
-
const i = pos.x + tileImage.width*(15 + tileLayer.size.y - pos.y);
|
|
46
|
-
if (!imageData[4*i])
|
|
47
|
-
continue;
|
|
48
|
-
|
|
49
|
-
// set tile data
|
|
50
|
-
const tileIndex = 1;
|
|
51
|
-
const direction = LJS.randInt(4)
|
|
52
|
-
const mirror = !LJS.randInt(2);
|
|
53
|
-
const color = LJS.randColor();
|
|
54
|
-
const data = new LJS.TileLayerData(tileIndex, direction, mirror, color);
|
|
55
|
-
tileLayer.setData(pos, data);
|
|
56
|
-
tileLayer.setCollisionData(pos);
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
// draw tile layer with new data
|
|
60
|
-
tileLayer.redraw();
|
|
61
|
-
|
|
62
|
-
// move camera to center of collision
|
|
63
|
-
LJS.setCameraPos(tileLayer.size.scale(.5));
|
|
64
|
-
LJS.setCameraScale(32);
|
|
65
|
-
|
|
66
|
-
// enable gravity
|
|
67
|
-
LJS.setGravity(vec2(0,-.01));
|
|
68
|
-
|
|
69
|
-
// create particle emitter
|
|
70
|
-
particleEmitter = new LJS.ParticleEmitter(
|
|
71
|
-
vec2(16,9), 0, // emitPos, emitAngle
|
|
72
|
-
0, 0, 500, 3.14, // emitSize, emitTime, rate, cone
|
|
73
|
-
tile(0, 16), // tileIndex, tileSize
|
|
74
|
-
hsl(1,1,1), hsl(0,0,0), // colorStartA, colorStartB
|
|
75
|
-
hsl(0,0,0,0), hsl(0,0,0,0), // colorEndA, colorEndB
|
|
76
|
-
1, .2, .2, .1, .05, // time, sizeStart, sizeEnd, speed, angleSpeed
|
|
77
|
-
.99, 1, 1, 3.14, // damping, angleDamping, gravityScale, cone
|
|
78
|
-
.05, .5, true, true // fadeRate, randomness, collide, additive
|
|
79
|
-
);
|
|
80
|
-
particleEmitter.restitution = .3; // bounce when it collides
|
|
81
|
-
particleEmitter.trailScale = 2; // stretch stretch as it moves
|
|
82
|
-
particleEmitter.velocityInheritance = .3; // inherit emitter velocity
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
///////////////////////////////////////////////////////////////////////////////
|
|
86
|
-
function gameUpdate()
|
|
87
|
-
{
|
|
88
|
-
if (LJS.mouseWasPressed(0))
|
|
89
|
-
{
|
|
90
|
-
// play sound when mouse is pressed
|
|
91
|
-
sound_click.play(LJS.mousePos);
|
|
92
|
-
|
|
93
|
-
// change particle color and set to fade out
|
|
94
|
-
particleEmitter.colorStartA = LJS.randColor();
|
|
95
|
-
particleEmitter.colorStartB = LJS.randColor();
|
|
96
|
-
particleEmitter.colorEndA = particleEmitter.colorStartA.scale(1,0);
|
|
97
|
-
particleEmitter.colorEndB = particleEmitter.colorStartB.scale(1,0);
|
|
98
|
-
|
|
99
|
-
// unlock medals
|
|
100
|
-
medal_example.unlock();
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
// move particles to mouse location if on screen
|
|
104
|
-
if (LJS.mousePosScreen.x)
|
|
105
|
-
particleEmitter.pos = LJS.mousePos;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
///////////////////////////////////////////////////////////////////////////////
|
|
109
|
-
function gameUpdatePost()
|
|
110
|
-
{
|
|
111
|
-
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
///////////////////////////////////////////////////////////////////////////////
|
|
115
|
-
function gameRender()
|
|
116
|
-
{
|
|
117
|
-
// draw a grey square in the background
|
|
118
|
-
LJS.drawRect(vec2(16,8), vec2(20,14), hsl(0,0,.6));
|
|
119
|
-
|
|
120
|
-
// draw the logo as a tile
|
|
121
|
-
LJS.drawTile(vec2(21,5), vec2(4.5), tile(3,128));
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
///////////////////////////////////////////////////////////////////////////////
|
|
125
|
-
function gameRenderPost()
|
|
126
|
-
{
|
|
127
|
-
LJS.drawTextScreen('LittleJS with TypeScript', vec2(LJS.mainCanvasSize.x/2, 80), 80);
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
///////////////////////////////////////////////////////////////////////////////
|
|
131
|
-
// Startup LittleJS Engine
|
|
132
|
-
LJS.engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRenderPost, ['tiles.png']);
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
<!DOCTYPE html><head>
|
|
2
|
-
<title>LittleJS TypeScript Demo</title>
|
|
3
|
-
<meta charset=utf-8>
|
|
4
|
-
<meta name=apple-mobile-web-app-capable content=yes>
|
|
5
|
-
<meta name=mobile-web-app-capable content=yes>
|
|
6
|
-
<link rel=icon type=image/png href=../favicon.png>
|
|
7
|
-
</head><body>
|
|
8
|
-
|
|
9
|
-
<!-- Add your game scripts here -->
|
|
10
|
-
<script src=game.js type=module></script>
|
|
Binary file
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compilerOptions": {
|
|
3
|
-
"allowJs": true,
|
|
4
|
-
"module": "es2020",
|
|
5
|
-
"target": "es2020",
|
|
6
|
-
"outDir": "build",
|
|
7
|
-
"moduleResolution": "bundler",
|
|
8
|
-
"skipLibCheck": true
|
|
9
|
-
},
|
|
10
|
-
"include": [
|
|
11
|
-
"*.ts"
|
|
12
|
-
],
|
|
13
|
-
"exclude": [
|
|
14
|
-
"node_modules",
|
|
15
|
-
"build"
|
|
16
|
-
]
|
|
17
|
-
}
|
|
@@ -1,139 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
Little JS UI System Example
|
|
3
|
-
- Shows how to use the LittleJS UI plugin
|
|
4
|
-
- Modal windows, buttons, text, checkboxes, and more
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
'use strict';
|
|
8
|
-
|
|
9
|
-
// import LittleJS module
|
|
10
|
-
import * as LJS from '../../dist/littlejs.esm.js';
|
|
11
|
-
const {vec2, hsl, tile} = LJS;
|
|
12
|
-
|
|
13
|
-
// UI system
|
|
14
|
-
let uiRoot, uiMenu;
|
|
15
|
-
const getMenuVisible =()=> uiMenu.visible;
|
|
16
|
-
const setMenuVisible =(visible)=> uiMenu.visible = visible;
|
|
17
|
-
|
|
18
|
-
// use a fixed size canvas
|
|
19
|
-
LJS.setCanvasFixedSize(vec2(1920, 1080)); // 1080p
|
|
20
|
-
LJS.setCanvasPixelated(false);
|
|
21
|
-
|
|
22
|
-
function createUI()
|
|
23
|
-
{
|
|
24
|
-
LJS.uiSystem.defaultSoundPress = new LJS.Sound([.5,0,220]);
|
|
25
|
-
LJS.uiSystem.defaultSoundClick = new LJS.Sound([.5,0,440]);
|
|
26
|
-
LJS.uiSystem.defaultCornerRadius = 8;
|
|
27
|
-
LJS.uiSystem.defaultGradientColor = LJS.WHITE;
|
|
28
|
-
LJS.uiSystem.defaultShadowColor = LJS.BLACK;
|
|
29
|
-
|
|
30
|
-
// setup root to attach all ui elements to
|
|
31
|
-
uiRoot = new LJS.UIObject;
|
|
32
|
-
const uiInfo = new LJS.UIText(vec2(0,90), vec2(1e3, 70),
|
|
33
|
-
'LittleJS UI System Example\nM = Toggle menu');
|
|
34
|
-
uiInfo.textColor = LJS.WHITE;
|
|
35
|
-
uiInfo.textLineWidth = 8;
|
|
36
|
-
uiRoot.addChild(uiInfo);
|
|
37
|
-
|
|
38
|
-
// setup example menu
|
|
39
|
-
uiMenu = new LJS.UIObject(vec2(0,500));
|
|
40
|
-
uiRoot.addChild(uiMenu);
|
|
41
|
-
const uiBackground = new LJS.UIObject(vec2(), vec2(450,580));
|
|
42
|
-
uiBackground.lineWidth = 8;
|
|
43
|
-
uiMenu.addChild(uiBackground);
|
|
44
|
-
|
|
45
|
-
// example large text
|
|
46
|
-
const textTitle = new LJS.UIText(vec2(0,-220), vec2(400, 120), 'Test Title');
|
|
47
|
-
uiMenu.addChild(textTitle);
|
|
48
|
-
textTitle.textColor = LJS.RED;
|
|
49
|
-
textTitle.textLineColor = LJS.BLUE;
|
|
50
|
-
textTitle.textLineWidth = 4;
|
|
51
|
-
|
|
52
|
-
// example multiline text
|
|
53
|
-
const textTest = new LJS.UIText(vec2(-60,-120), vec2(300, 60), 'Test Text\nSecond text line.')
|
|
54
|
-
uiMenu.addChild(textTest);
|
|
55
|
-
|
|
56
|
-
// example tile image
|
|
57
|
-
const tileTest = new LJS.UITile(vec2(150,-130), vec2(110), tile(3,128))
|
|
58
|
-
uiMenu.addChild(tileTest);
|
|
59
|
-
|
|
60
|
-
// setup navigation index for gamepad and keyboard navigation
|
|
61
|
-
let navigationIndex = 0;
|
|
62
|
-
|
|
63
|
-
// example checkbox
|
|
64
|
-
const checkbox = new LJS.UICheckbox(vec2(-140,-20), vec2(50));
|
|
65
|
-
uiMenu.addChild(checkbox);
|
|
66
|
-
checkbox.onChange = ()=> button1.disabled = checkbox.checked;
|
|
67
|
-
checkbox.navigationIndex = ++navigationIndex;
|
|
68
|
-
checkbox.text = 'Test Checkbox';
|
|
69
|
-
|
|
70
|
-
// example slider
|
|
71
|
-
const slider = new LJS.UISlider(vec2(0,60), vec2(350, 50));
|
|
72
|
-
uiMenu.addChild(slider);
|
|
73
|
-
slider.onChange = ()=> slider.text = slider.value.toFixed(2)
|
|
74
|
-
slider.onChange();
|
|
75
|
-
slider.navigationIndex = ++navigationIndex;
|
|
76
|
-
|
|
77
|
-
// example button
|
|
78
|
-
const button1 = new LJS.UIButton(vec2(0,140), vec2(350, 50), 'Test Button');
|
|
79
|
-
uiMenu.addChild(button1);
|
|
80
|
-
button1.onClick = ()=> uiBackground.color = hsl(LJS.rand(),1,.7);
|
|
81
|
-
button1.navigationIndex = ++navigationIndex;
|
|
82
|
-
|
|
83
|
-
// exit button
|
|
84
|
-
const button2 = new LJS.UIButton(vec2(0,220), vec2(350, 50), 'Exit Menu');
|
|
85
|
-
uiMenu.addChild(button2);
|
|
86
|
-
button2.onClick = ()=> LJS.uiSystem.showConfirmDialog('Exit menu?',
|
|
87
|
-
()=> setMenuVisible(false));
|
|
88
|
-
button2.navigationIndex = ++navigationIndex;
|
|
89
|
-
button2.navigationAutoSelect = true;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
///////////////////////////////////////////////////////////////////////////////
|
|
93
|
-
function gameInit()
|
|
94
|
-
{
|
|
95
|
-
new LJS.UISystemPlugin;
|
|
96
|
-
createUI();
|
|
97
|
-
LJS.setCanvasClearColor(hsl(0,0,.2));
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
///////////////////////////////////////////////////////////////////////////////
|
|
101
|
-
function gameUpdate()
|
|
102
|
-
{
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
///////////////////////////////////////////////////////////////////////////////
|
|
106
|
-
function gameUpdatePost()
|
|
107
|
-
{
|
|
108
|
-
if (LJS.keyWasPressed('KeyM') && !LJS.uiSystem.confirmDialog)
|
|
109
|
-
{
|
|
110
|
-
// toggle menu visibility
|
|
111
|
-
setMenuVisible(!getMenuVisible());
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
// center ui
|
|
115
|
-
uiRoot.pos.x = LJS.mainCanvasSize.x/2;
|
|
116
|
-
|
|
117
|
-
// pause when menu is visible
|
|
118
|
-
LJS.setPaused(getMenuVisible())
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
///////////////////////////////////////////////////////////////////////////////
|
|
122
|
-
function gameRender()
|
|
123
|
-
{
|
|
124
|
-
// test game rendering
|
|
125
|
-
for (let i=0; i<1e3; ++i)
|
|
126
|
-
{
|
|
127
|
-
const pos = vec2(30*LJS.sin(i+LJS.time/9),20*LJS.sin(i*i+LJS.time/9));
|
|
128
|
-
LJS.drawTile(pos, vec2(2), tile(3,128), hsl(i/9,1,.4), LJS.time+i, !(i%2), hsl(i/9,1,.1,0));
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
///////////////////////////////////////////////////////////////////////////////
|
|
133
|
-
function gameRenderPost()
|
|
134
|
-
{
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
///////////////////////////////////////////////////////////////////////////////
|
|
138
|
-
// Startup LittleJS Engine
|
|
139
|
-
LJS.engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRenderPost, ['tiles.png']);
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
<!DOCTYPE html><head>
|
|
2
|
-
<title>LittleJS Starter Project</title>
|
|
3
|
-
<meta charset=utf-8>
|
|
4
|
-
<meta name=apple-mobile-web-app-capable content=yes>
|
|
5
|
-
<meta name=mobile-web-app-capable content=yes>
|
|
6
|
-
<link rel=icon type=image/png href=../favicon.png>
|
|
7
|
-
</head><body>
|
|
8
|
-
|
|
9
|
-
<!-- Add your game scripts here -->
|
|
10
|
-
<script src=game.js type=module></script>
|
|
Binary file
|