littlejsengine 1.14.10 → 1.14.16
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/dist/littlejs.d.ts +264 -120
- package/dist/littlejs.esm.js +603 -288
- package/dist/littlejs.esm.min.js +1 -1
- package/dist/littlejs.js +600 -287
- package/dist/littlejs.min.js +1 -1
- package/dist/littlejs.release.js +521 -232
- package/examples/box2d/gameObjects.js +2 -2
- package/examples/breakout/gameObjects.js +2 -2
- package/examples/breakoutTutorial/README.md +32 -32
- package/examples/breakoutTutorial/game.js +1 -1
- package/examples/electron/game.js +3 -3
- package/examples/electron/index.html +2 -2
- package/examples/electron/package.json +1 -8
- package/examples/index.html +59 -51
- package/examples/module/game.js +3 -3
- package/examples/platformer/gameCharacter.js +6 -2
- package/examples/platformer/gameEffects.js +4 -4
- package/examples/platformer/gameLevel.js +1 -1
- package/examples/platformer/gameObjects.js +11 -9
- package/examples/puzzle/game.js +1 -1
- package/examples/shorts/animation.js +1 -1
- package/examples/shorts/base.html +1 -1
- package/examples/shorts/blending.js +8 -14
- package/examples/shorts/box2d.js +7 -3
- package/examples/shorts/box2dCar.js +2 -1
- package/examples/shorts/empty.js +30 -0
- package/examples/shorts/helloWorld.js +1 -1
- package/examples/shorts/hillGlideGame.js +10 -4
- package/examples/shorts/landerGame.js +30 -3
- package/examples/shorts/medals.js +4 -4
- package/examples/shorts/music.js +29 -55
- package/examples/shorts/musicPlayer.js +134 -0
- package/examples/shorts/nineSlice.js +34 -15
- package/examples/shorts/parallax.js +4 -3
- package/examples/shorts/particles.js +16 -16
- package/examples/shorts/piano.js +15 -21
- package/examples/shorts/pongGame.js +6 -4
- package/examples/shorts/raycasting.js +13 -5
- package/examples/shorts/sequencer.js +122 -0
- package/examples/shorts/shapes.js +7 -4
- package/examples/shorts/slidingPuzzle.js +16 -6
- package/examples/shorts/sound.js +18 -9
- package/examples/shorts/spaceGame.js +12 -8
- package/examples/shorts/spriteAtlas.js +7 -7
- package/examples/shorts/starfield.js +1 -1
- package/examples/shorts/systemFont.js +2 -2
- package/examples/shorts/texture.js +2 -2
- package/examples/shorts/tileLayer.js +10 -10
- package/examples/shorts/tiltedView.js +14 -3
- package/examples/shorts/timers.js +10 -0
- package/examples/shorts/topDown.js +4 -1
- package/examples/shorts/uiSystem.js +8 -5
- package/examples/starter/game.js +3 -3
- package/examples/starter/index.html +2 -2
- package/examples/typescript/game.js +3 -3
- package/examples/typescript/game.ts +3 -3
- package/examples/uiSystem/game.js +6 -6
- package/package.json +4 -2
- package/plugins/box2d.js +18 -3
- package/plugins/newgrounds.js +11 -9
- package/plugins/postProcess.js +5 -2
- package/plugins/uiSystem.js +120 -34
- package/plugins/zzfxm.js +5 -1
- package/reference.md +1 -3
- package/src/engine.js +52 -21
- package/src/engineAudio.js +31 -14
- package/src/engineDebug.js +80 -55
- package/src/engineDraw.js +60 -31
- package/src/engineExport.js +3 -1
- package/src/engineMedals.js +10 -2
- package/src/engineObject.js +14 -10
- package/src/engineParticles.js +59 -46
- package/src/engineRelease.js +1 -0
- package/src/engineSettings.js +7 -7
- package/src/engineTileLayer.js +49 -18
- package/src/engineUtilities.js +79 -34
package/dist/littlejs.release.js
CHANGED
|
@@ -33,7 +33,7 @@ const engineName = 'LittleJS';
|
|
|
33
33
|
* @type {string}
|
|
34
34
|
* @default
|
|
35
35
|
* @memberof Engine */
|
|
36
|
-
const engineVersion = '1.14.
|
|
36
|
+
const engineVersion = '1.14.16';
|
|
37
37
|
|
|
38
38
|
/** Frames per second to update
|
|
39
39
|
* @type {number}
|
|
@@ -96,9 +96,14 @@ let frameTimeLastMS = 0, frameTimeBufferMS = 0, averageFPS = 0;
|
|
|
96
96
|
|
|
97
97
|
const pluginUpdateList = [], pluginRenderList = [];
|
|
98
98
|
|
|
99
|
+
/**
|
|
100
|
+
* @callback PluginCallback - Update or render function for a plugin
|
|
101
|
+
* @memberof Engine
|
|
102
|
+
*/
|
|
103
|
+
|
|
99
104
|
/** Add a new update function for a plugin
|
|
100
|
-
* @param {
|
|
101
|
-
* @param {
|
|
105
|
+
* @param {PluginCallback} [updateFunction]
|
|
106
|
+
* @param {PluginCallback} [renderFunction]
|
|
102
107
|
* @memberof Engine */
|
|
103
108
|
function engineAddPlugin(updateFunction, renderFunction)
|
|
104
109
|
{
|
|
@@ -111,23 +116,33 @@ function engineAddPlugin(updateFunction, renderFunction)
|
|
|
111
116
|
///////////////////////////////////////////////////////////////////////////////
|
|
112
117
|
// Main Engine Functions
|
|
113
118
|
|
|
119
|
+
/**
|
|
120
|
+
* @callback GameInitCallback - Called after the engine starts, can be async
|
|
121
|
+
* @returns {void|Promise<void>}
|
|
122
|
+
* @memberof Engine
|
|
123
|
+
*/
|
|
124
|
+
/**
|
|
125
|
+
* @callback GameCallback - Update or render function for the game
|
|
126
|
+
* @memberof Engine
|
|
127
|
+
*/
|
|
128
|
+
|
|
114
129
|
/** Startup LittleJS engine with your callback functions
|
|
115
|
-
* @param {
|
|
116
|
-
* @param {
|
|
117
|
-
* @param {
|
|
118
|
-
* @param {
|
|
119
|
-
* @param {
|
|
130
|
+
* @param {GameInitCallback} gameInit - Called once after the engine starts up, can be async for loading
|
|
131
|
+
* @param {GameCallback} gameUpdate - Called every frame before objects are updated (60fps), use for game logic
|
|
132
|
+
* @param {GameCallback} gameUpdatePost - Called after physics and objects are updated, even when paused, use for UI updates
|
|
133
|
+
* @param {GameCallback} gameRender - Called before objects are rendered, use for drawing backgrounds/world elements
|
|
134
|
+
* @param {GameCallback} gameRenderPost - Called after objects are rendered, use for drawing UI/overlays
|
|
120
135
|
* @param {Array<string>} [imageSources=[]] - List of image file paths to preload (e.g., ['player.png', 'tiles.png'])
|
|
121
136
|
* @param {HTMLElement} [rootElement] - Root DOM element to attach canvas to, defaults to document.body
|
|
122
137
|
* @example
|
|
123
138
|
* // Basic engine startup
|
|
124
139
|
* engineInit(
|
|
125
|
-
* () => {
|
|
126
|
-
* () => { updateGameLogic(); },
|
|
127
|
-
* () => { updateUI(); },
|
|
128
|
-
* () => { drawBackground(); },
|
|
129
|
-
* () => { drawHUD(); },
|
|
130
|
-
* ['tiles.png', 'tilesLevel.png']
|
|
140
|
+
* () => { LOG('Game initialized!'); }, // gameInit
|
|
141
|
+
* () => { updateGameLogic(); }, // gameUpdate
|
|
142
|
+
* () => { updateUI(); }, // gameUpdatePost
|
|
143
|
+
* () => { drawBackground(); }, // gameRender
|
|
144
|
+
* () => { drawHUD(); }, // gameRenderPost
|
|
145
|
+
* ['tiles.png', 'tilesLevel.png'] // images to load
|
|
131
146
|
* );
|
|
132
147
|
* @memberof Engine */
|
|
133
148
|
async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRenderPost, imageSources=[], rootElement=document.body)
|
|
@@ -173,16 +188,20 @@ async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, game
|
|
|
173
188
|
if (!debugSpeedUp)
|
|
174
189
|
frameTimeBufferMS = min(frameTimeBufferMS, 50); // clamp min framerate
|
|
175
190
|
|
|
191
|
+
let wasUpdated = false;
|
|
176
192
|
if (paused)
|
|
177
193
|
{
|
|
194
|
+
// update everything except the game and objects
|
|
195
|
+
wasUpdated = true;
|
|
178
196
|
updateCanvas();
|
|
197
|
+
inputUpdate();
|
|
198
|
+
pluginUpdateList.forEach(f=>f());
|
|
179
199
|
|
|
180
200
|
// update object transforms even when paused
|
|
181
201
|
for (const o of engineObjects)
|
|
182
202
|
o.parent || o.updateTransforms();
|
|
183
203
|
|
|
184
|
-
|
|
185
|
-
pluginUpdateList.forEach(f=>f());
|
|
204
|
+
// do post update
|
|
186
205
|
debugUpdate();
|
|
187
206
|
gameUpdatePost();
|
|
188
207
|
inputUpdatePost();
|
|
@@ -199,12 +218,13 @@ async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, game
|
|
|
199
218
|
}
|
|
200
219
|
|
|
201
220
|
// update multiple frames if necessary in case of slow framerate
|
|
202
|
-
for (;frameTimeBufferMS >= 0; frameTimeBufferMS -= 1e3 / frameRate)
|
|
221
|
+
for (; frameTimeBufferMS >= 0; frameTimeBufferMS -= 1e3 / frameRate)
|
|
203
222
|
{
|
|
204
223
|
// increment frame and update time
|
|
205
224
|
time = frame++ / frameRate;
|
|
206
225
|
|
|
207
226
|
// update game and objects
|
|
227
|
+
wasUpdated = true;
|
|
208
228
|
updateCanvas();
|
|
209
229
|
inputUpdate();
|
|
210
230
|
gameUpdate();
|
|
@@ -215,7 +235,6 @@ async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, game
|
|
|
215
235
|
debugUpdate();
|
|
216
236
|
gameUpdatePost();
|
|
217
237
|
inputUpdatePost();
|
|
218
|
-
|
|
219
238
|
if (debugVideoCaptureIsActive())
|
|
220
239
|
renderFrame();
|
|
221
240
|
}
|
|
@@ -232,6 +251,10 @@ async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, game
|
|
|
232
251
|
{
|
|
233
252
|
if (headlessMode) return;
|
|
234
253
|
|
|
254
|
+
// canvas must be updated before rendering
|
|
255
|
+
if (!wasUpdated)
|
|
256
|
+
updateCanvas();
|
|
257
|
+
|
|
235
258
|
// render sort then render while removing destroyed objects
|
|
236
259
|
enginePreRender();
|
|
237
260
|
gameRender();
|
|
@@ -360,6 +383,8 @@ async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, game
|
|
|
360
383
|
const promises = imageSources.map((src, textureIndex)=>
|
|
361
384
|
new Promise(resolve =>
|
|
362
385
|
{
|
|
386
|
+
ASSERT(isString(src), 'imageSources must be an array of strings');
|
|
387
|
+
|
|
363
388
|
const image = new Image;
|
|
364
389
|
image.onerror = image.onload = ()=>
|
|
365
390
|
{
|
|
@@ -475,10 +500,16 @@ function engineObjectsCollect(pos, size, objects=engineObjects)
|
|
|
475
500
|
return collectedObjects;
|
|
476
501
|
}
|
|
477
502
|
|
|
503
|
+
/**
|
|
504
|
+
* @callback ObjectCallbackFunction - Function that processes an object
|
|
505
|
+
* @param {EngineObject} uiObjects
|
|
506
|
+
* @memberof Engine
|
|
507
|
+
*/
|
|
508
|
+
|
|
478
509
|
/** Triggers a callback for each object within a given area
|
|
479
|
-
* @param {Vector2} [pos]
|
|
480
|
-
* @param {Vector2|number} [size]
|
|
481
|
-
* @param {
|
|
510
|
+
* @param {Vector2} [pos] - Center of test area, or undefined for all objects
|
|
511
|
+
* @param {Vector2|number} [size] - Radius of circle if float, rectangle size if Vector2
|
|
512
|
+
* @param {ObjectCallbackFunction} [callbackFunction] - Calls this function on every object that passes the test
|
|
482
513
|
* @param {Array<EngineObject>} [objects=engineObjects] - List of objects to check
|
|
483
514
|
* @memberof Engine */
|
|
484
515
|
function engineObjectsCallback(pos, size, callbackFunction, objects=engineObjects)
|
|
@@ -690,6 +721,7 @@ const debugMedals = 0;
|
|
|
690
721
|
|
|
691
722
|
// debug commands are automatically removed from the final build
|
|
692
723
|
function ASSERT (){}
|
|
724
|
+
function LOG (){}
|
|
693
725
|
function debugInit (){}
|
|
694
726
|
function debugUpdate (){}
|
|
695
727
|
function debugRender (){}
|
|
@@ -927,7 +959,12 @@ function wave(frequency=1, amplitude=1, t=time, offset=0)
|
|
|
927
959
|
* @param {number} t - time in seconds
|
|
928
960
|
* @return {string}
|
|
929
961
|
* @memberof Utilities */
|
|
930
|
-
function formatTime(t)
|
|
962
|
+
function formatTime(t)
|
|
963
|
+
{
|
|
964
|
+
const sign = t < 0 ? '-' : '';
|
|
965
|
+
t = abs(t)|0;
|
|
966
|
+
return sign + (t/60|0) + ':' + (t%60<10?'0':'') + t%60;
|
|
967
|
+
}
|
|
931
968
|
|
|
932
969
|
/** Fetches a JSON file from a URL and returns the parsed JSON object. Must be used with await!
|
|
933
970
|
* @param {string} url - URL of JSON file
|
|
@@ -936,6 +973,8 @@ function formatTime(t) { return (t/60|0) + ':' + (t%60<10?'0':'') + (t%60|0); }
|
|
|
936
973
|
async function fetchJSON(url)
|
|
937
974
|
{
|
|
938
975
|
const response = await fetch(url);
|
|
976
|
+
if (!response.ok)
|
|
977
|
+
throw new Error(`Failed to fetch JSON from ${url}: ${response.status} ${response.statusText}`);
|
|
939
978
|
return response.json();
|
|
940
979
|
}
|
|
941
980
|
|
|
@@ -946,6 +985,13 @@ async function fetchJSON(url)
|
|
|
946
985
|
* @memberof Utilities */
|
|
947
986
|
function isNumber(n) { return typeof n === 'number' && !isNaN(n); }
|
|
948
987
|
|
|
988
|
+
/**
|
|
989
|
+
* Check if object is a valid string or can be converted to one
|
|
990
|
+
* @param {any} s
|
|
991
|
+
* @return {boolean}
|
|
992
|
+
* @memberof Utilities */
|
|
993
|
+
function isString(s) { return s !== undefined && s !== null && typeof s.toString() === 'string'; }
|
|
994
|
+
|
|
949
995
|
///////////////////////////////////////////////////////////////////////////////
|
|
950
996
|
|
|
951
997
|
/** Random global functions
|
|
@@ -1008,6 +1054,7 @@ function randColor(colorA=new Color, colorB=new Color(0,0,0,1), linear=false)
|
|
|
1008
1054
|
/**
|
|
1009
1055
|
* Seeded random number generator
|
|
1010
1056
|
* - Can be used to create a deterministic random number sequence
|
|
1057
|
+
* @memberof Engine
|
|
1011
1058
|
* @example
|
|
1012
1059
|
* let r = new RandomGenerator(123); // random number generator with seed 123
|
|
1013
1060
|
* let a = r.float(); // random value between 0 and 1
|
|
@@ -1104,6 +1151,7 @@ function ASSERT_VECTOR2_NORMAL(v)
|
|
|
1104
1151
|
/**
|
|
1105
1152
|
* 2D Vector object with vector math library
|
|
1106
1153
|
* - Functions do not change this so they can be chained together
|
|
1154
|
+
* @memberof Engine
|
|
1107
1155
|
* @example
|
|
1108
1156
|
* let a = new Vector2(2, 3); // vector with coordinates (2, 3)
|
|
1109
1157
|
* let b = new Vector2; // vector with coordinates (0, 0)
|
|
@@ -1244,9 +1292,10 @@ class Vector2
|
|
|
1244
1292
|
return new Vector2(this.x*c - this.y*s, this.x*s + this.y*c);
|
|
1245
1293
|
}
|
|
1246
1294
|
|
|
1247
|
-
/**
|
|
1295
|
+
/** Sets this this vector to point in the specified integer direction (0-3), corresponding to multiples of 90 degree rotation
|
|
1248
1296
|
* @param {number} [direction]
|
|
1249
|
-
* @param {number} [length]
|
|
1297
|
+
* @param {number} [length]
|
|
1298
|
+
* @return {Vector2} */
|
|
1250
1299
|
setDirection(direction, length=1)
|
|
1251
1300
|
{
|
|
1252
1301
|
ASSERT_NUMBER_VALID(direction);
|
|
@@ -1254,8 +1303,10 @@ class Vector2
|
|
|
1254
1303
|
direction = mod(direction, 4);
|
|
1255
1304
|
ASSERT(direction===0 || direction===1 || direction===2 || direction===3,
|
|
1256
1305
|
'Vector2.setDirection() direction must be an integer between 0 and 3.');
|
|
1257
|
-
|
|
1258
|
-
|
|
1306
|
+
|
|
1307
|
+
this.x = direction%2 ? direction-1 ? -length : length : 0;
|
|
1308
|
+
this.y = direction%2 ? 0 : direction ? -length : length;
|
|
1309
|
+
return this;
|
|
1259
1310
|
}
|
|
1260
1311
|
|
|
1261
1312
|
/** Returns the integer direction of this vector, corresponding to multiples of 90 degree rotation (0-3)
|
|
@@ -1263,11 +1314,7 @@ class Vector2
|
|
|
1263
1314
|
direction()
|
|
1264
1315
|
{ return abs(this.x) > abs(this.y) ? this.x < 0 ? 3 : 1 : this.y < 0 ? 2 : 0; }
|
|
1265
1316
|
|
|
1266
|
-
/** Returns a copy of this vector
|
|
1267
|
-
* @return {Vector2} */
|
|
1268
|
-
invert() { return new Vector2(this.y, -this.x); }
|
|
1269
|
-
|
|
1270
|
-
/** Returns a copy of this vector absolute values
|
|
1317
|
+
/** Returns a copy of this vector with absolute values
|
|
1271
1318
|
* @return {Vector2} */
|
|
1272
1319
|
abs() { return new Vector2(abs(this.x), abs(this.y)); }
|
|
1273
1320
|
|
|
@@ -1309,13 +1356,10 @@ class Vector2
|
|
|
1309
1356
|
toString(digits=3)
|
|
1310
1357
|
{
|
|
1311
1358
|
ASSERT_NUMBER_VALID(digits);
|
|
1312
|
-
if (
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
else
|
|
1317
|
-
return `(${this.x}, ${this.y})`;
|
|
1318
|
-
}
|
|
1359
|
+
if (this.isValid())
|
|
1360
|
+
return `(${(this.x<0?'':' ') + this.x.toFixed(digits)},${(this.y<0?'':' ') + this.y.toFixed(digits)} )`;
|
|
1361
|
+
else
|
|
1362
|
+
return `(${this.x}, ${this.y})`;
|
|
1319
1363
|
}
|
|
1320
1364
|
|
|
1321
1365
|
/** Checks if this is a valid vector
|
|
@@ -1358,6 +1402,7 @@ function ASSERT_COLOR_VALID(c) { ASSERT(isColor(c), 'Color is invalid.', c); }
|
|
|
1358
1402
|
|
|
1359
1403
|
/**
|
|
1360
1404
|
* Color object (red, green, blue, alpha) with some helpful functions
|
|
1405
|
+
* @memberof Engine
|
|
1361
1406
|
* @example
|
|
1362
1407
|
* let a = new Color; // white
|
|
1363
1408
|
* let b = new Color(1, 0, 0); // red
|
|
@@ -1535,7 +1580,8 @@ class Color
|
|
|
1535
1580
|
* @return {Color} */
|
|
1536
1581
|
setHex(hex)
|
|
1537
1582
|
{
|
|
1538
|
-
ASSERT(
|
|
1583
|
+
ASSERT(isString(hex), 'Color hex code must be a string');
|
|
1584
|
+
ASSERT(hex[0] === '#', 'Color hex code must start with #');
|
|
1539
1585
|
ASSERT([4,5,7,9].includes(hex.length), 'Invalid hex');
|
|
1540
1586
|
|
|
1541
1587
|
if (hex.length < 6)
|
|
@@ -1577,77 +1623,78 @@ class Color
|
|
|
1577
1623
|
}
|
|
1578
1624
|
|
|
1579
1625
|
///////////////////////////////////////////////////////////////////////////////
|
|
1580
|
-
//
|
|
1626
|
+
// Default Colors
|
|
1581
1627
|
|
|
1582
1628
|
/** Color - White #ffffff
|
|
1583
1629
|
* @type {Color}
|
|
1584
1630
|
* @memberof Utilities */
|
|
1585
|
-
const WHITE = rgb();
|
|
1631
|
+
const WHITE = protectEngineConstant(rgb());
|
|
1586
1632
|
|
|
1587
|
-
/** Color - Clear White #
|
|
1633
|
+
/** Color - Clear White #757474ff with 0 alpha
|
|
1588
1634
|
* @type {Color}
|
|
1589
1635
|
* @memberof Utilities */
|
|
1590
|
-
const CLEAR_WHITE = rgb(1,1,1,0);
|
|
1636
|
+
const CLEAR_WHITE = protectEngineConstant(rgb(1,1,1,0));
|
|
1591
1637
|
|
|
1592
1638
|
/** Color - Black #000000
|
|
1593
1639
|
* @type {Color}
|
|
1594
1640
|
* @memberof Utilities */
|
|
1595
|
-
const BLACK = rgb(0,0,0);
|
|
1641
|
+
const BLACK = protectEngineConstant(rgb(0,0,0));
|
|
1596
1642
|
|
|
1597
1643
|
/** Color - Clear Black #000000 with 0 alpha
|
|
1598
1644
|
* @type {Color}
|
|
1599
1645
|
* @memberof Utilities */
|
|
1600
|
-
const CLEAR_BLACK = rgb(0,0,0,0);
|
|
1646
|
+
const CLEAR_BLACK = protectEngineConstant(rgb(0,0,0,0));
|
|
1601
1647
|
|
|
1602
1648
|
/** Color - Gray #808080
|
|
1603
1649
|
* @type {Color}
|
|
1604
1650
|
* @memberof Utilities */
|
|
1605
|
-
const GRAY = rgb(.5,.5,.5);
|
|
1651
|
+
const GRAY = protectEngineConstant(rgb(.5,.5,.5));
|
|
1606
1652
|
|
|
1607
1653
|
/** Color - Red #ff0000
|
|
1608
1654
|
* @type {Color}
|
|
1609
1655
|
* @memberof Utilities */
|
|
1610
|
-
const RED = rgb(1,0,0);
|
|
1656
|
+
const RED = protectEngineConstant(rgb(1,0,0));
|
|
1611
1657
|
|
|
1612
1658
|
/** Color - Orange #ff8000
|
|
1613
1659
|
* @type {Color}
|
|
1614
1660
|
* @memberof Utilities */
|
|
1615
|
-
const ORANGE = rgb(1,.5,0);
|
|
1661
|
+
const ORANGE = protectEngineConstant(rgb(1,.5,0));
|
|
1616
1662
|
|
|
1617
1663
|
/** Color - Yellow #ffff00
|
|
1618
1664
|
* @type {Color}
|
|
1619
1665
|
* @memberof Utilities */
|
|
1620
|
-
const YELLOW = rgb(1,1,0);
|
|
1666
|
+
const YELLOW = protectEngineConstant(rgb(1,1,0));
|
|
1621
1667
|
|
|
1622
1668
|
/** Color - Green #00ff00
|
|
1623
1669
|
* @type {Color}
|
|
1624
1670
|
* @memberof Utilities */
|
|
1625
|
-
const GREEN = rgb(0,1,0);
|
|
1671
|
+
const GREEN = protectEngineConstant(rgb(0,1,0));
|
|
1626
1672
|
|
|
1627
1673
|
/** Color - Cyan #00ffff
|
|
1628
1674
|
* @type {Color}
|
|
1629
1675
|
* @memberof Utilities */
|
|
1630
|
-
const CYAN = rgb(0,1,1);
|
|
1676
|
+
const CYAN = protectEngineConstant(rgb(0,1,1));
|
|
1631
1677
|
|
|
1632
1678
|
/** Color - Blue #0000ff
|
|
1633
1679
|
* @type {Color}
|
|
1634
1680
|
* @memberof Utilities */
|
|
1635
|
-
const BLUE = rgb(0,0,1);
|
|
1681
|
+
const BLUE = protectEngineConstant(rgb(0,0,1));
|
|
1636
1682
|
|
|
1637
1683
|
/** Color - Purple #8000ff
|
|
1638
1684
|
* @type {Color}
|
|
1639
1685
|
* @memberof Utilities */
|
|
1640
|
-
const PURPLE = rgb(.5,0,1);
|
|
1686
|
+
const PURPLE = protectEngineConstant(rgb(.5,0,1));
|
|
1641
1687
|
|
|
1642
1688
|
/** Color - Magenta #ff00ff
|
|
1643
1689
|
* @type {Color}
|
|
1644
1690
|
* @memberof Utilities */
|
|
1645
|
-
const MAGENTA = rgb(1,0,1);
|
|
1691
|
+
const MAGENTA = protectEngineConstant(rgb(1,0,1));
|
|
1646
1692
|
|
|
1647
1693
|
///////////////////////////////////////////////////////////////////////////////
|
|
1648
1694
|
|
|
1649
1695
|
/**
|
|
1650
1696
|
* Timer object tracks how long has passed since it was set
|
|
1697
|
+
* @memberof Engine
|
|
1651
1698
|
* @example
|
|
1652
1699
|
* let a = new Timer; // creates a timer that is not set
|
|
1653
1700
|
* a.set(3); // sets the timer to 3 seconds
|
|
@@ -1704,11 +1751,41 @@ class Timer
|
|
|
1704
1751
|
|
|
1705
1752
|
/** Returns this timer expressed as a string
|
|
1706
1753
|
* @return {string} */
|
|
1707
|
-
toString() {
|
|
1754
|
+
toString() { return this.isSet() ? Math.abs(this.get()) + ' seconds ' + (this.get()<0 ? 'before' : 'after' ) : 'unset'; }
|
|
1708
1755
|
|
|
1709
1756
|
/** Get how long since elapsed, returns 0 if not set (returns negative if currently active)
|
|
1710
1757
|
* @return {number} */
|
|
1711
1758
|
valueOf() { return this.get(); }
|
|
1759
|
+
}
|
|
1760
|
+
|
|
1761
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
1762
|
+
// Helper functions used by the engine
|
|
1763
|
+
|
|
1764
|
+
// make color constants immutable with debug assertions
|
|
1765
|
+
function protectEngineConstant(obj)
|
|
1766
|
+
{
|
|
1767
|
+
if (debug)
|
|
1768
|
+
{
|
|
1769
|
+
// get properties and store original values
|
|
1770
|
+
const props = Object.keys(obj), values = {};
|
|
1771
|
+
props.forEach(prop => values[prop] = obj[prop]);
|
|
1772
|
+
|
|
1773
|
+
// replace with getters/setters that assert
|
|
1774
|
+
props.forEach(prop =>
|
|
1775
|
+
{
|
|
1776
|
+
Object.defineProperty(obj, prop, {
|
|
1777
|
+
get: () => values[prop],
|
|
1778
|
+
set: (value) =>
|
|
1779
|
+
{
|
|
1780
|
+
ASSERT(false, `Cannot modify engine constant. Attempted to set constant (${obj}) property '${prop}' to '${value}'.`);
|
|
1781
|
+
},
|
|
1782
|
+
enumerable: true
|
|
1783
|
+
});
|
|
1784
|
+
});
|
|
1785
|
+
}
|
|
1786
|
+
|
|
1787
|
+
// freeze the object to prevent adding new properties
|
|
1788
|
+
return Object.freeze(obj);
|
|
1712
1789
|
}
|
|
1713
1790
|
/**
|
|
1714
1791
|
* LittleJS Engine Settings
|
|
@@ -2016,7 +2093,7 @@ let medalsPreventUnlock = false;
|
|
|
2016
2093
|
/** Set position of camera in world space
|
|
2017
2094
|
* @param {Vector2} pos
|
|
2018
2095
|
* @memberof Settings */
|
|
2019
|
-
function setCameraPos(pos) { cameraPos = pos; }
|
|
2096
|
+
function setCameraPos(pos) { cameraPos = pos.copy(); }
|
|
2020
2097
|
|
|
2021
2098
|
/** Set angle of camera in world space
|
|
2022
2099
|
* @param {number} angle
|
|
@@ -2039,17 +2116,17 @@ function setCanvasColorTiles(colorTiles) { canvasColorTiles = colorTiles; }
|
|
|
2039
2116
|
/** Set color to clear the canvas to before render
|
|
2040
2117
|
* @param {Color} color
|
|
2041
2118
|
* @memberof Settings */
|
|
2042
|
-
function setCanvasClearColor(color) { canvasClearColor = color; }
|
|
2119
|
+
function setCanvasClearColor(color) { canvasClearColor = color.copy(); }
|
|
2043
2120
|
|
|
2044
2121
|
/** Set max size of the canvas
|
|
2045
2122
|
* @param {Vector2} size
|
|
2046
2123
|
* @memberof Settings */
|
|
2047
|
-
function setCanvasMaxSize(size) { canvasMaxSize = size; }
|
|
2124
|
+
function setCanvasMaxSize(size) { canvasMaxSize = size.copy(); }
|
|
2048
2125
|
|
|
2049
2126
|
/** Set fixed size of the canvas
|
|
2050
2127
|
* @param {Vector2} size
|
|
2051
2128
|
* @memberof Settings */
|
|
2052
|
-
function setCanvasFixedSize(size) { canvasFixedSize = size; }
|
|
2129
|
+
function setCanvasFixedSize(size) { canvasFixedSize = size.copy(); }
|
|
2053
2130
|
|
|
2054
2131
|
/** Use nearest scaling algorithm for canvas for more pixelated look
|
|
2055
2132
|
* - If enabled sets css image-rendering:pixelated
|
|
@@ -2114,7 +2191,7 @@ function setGLCircleSides(sides) { glCircleSides = sides; }
|
|
|
2114
2191
|
/** Set default size of tiles in pixels
|
|
2115
2192
|
* @param {Vector2} size
|
|
2116
2193
|
* @memberof Settings */
|
|
2117
|
-
function setTileSizeDefault(size) { tileSizeDefault = size; }
|
|
2194
|
+
function setTileSizeDefault(size) { tileSizeDefault = size.copy(); }
|
|
2118
2195
|
|
|
2119
2196
|
/** Set to prevent tile bleeding from neighbors in pixels
|
|
2120
2197
|
* @param {number} scale
|
|
@@ -2159,7 +2236,7 @@ function setObjectMaxSpeed(speed) { objectMaxSpeed = speed; }
|
|
|
2159
2236
|
/** Set how much gravity to apply to objects
|
|
2160
2237
|
* @param {Vector2} newGravity
|
|
2161
2238
|
* @memberof Settings */
|
|
2162
|
-
function setGravity(newGravity) { gravity = newGravity; }
|
|
2239
|
+
function setGravity(newGravity) { gravity = newGravity.copy(); }
|
|
2163
2240
|
|
|
2164
2241
|
/** Set to scales emit rate of particles
|
|
2165
2242
|
* @param {number} scale
|
|
@@ -2249,7 +2326,7 @@ function setMedalDisplaySlideTime(time) { medalDisplaySlideTime = time; }
|
|
|
2249
2326
|
/** Set size of medal display
|
|
2250
2327
|
* @param {Vector2} size
|
|
2251
2328
|
* @memberof Settings */
|
|
2252
|
-
function setMedalDisplaySize(size) { medalDisplaySize = size; }
|
|
2329
|
+
function setMedalDisplaySize(size) { medalDisplaySize = size.copy(); }
|
|
2253
2330
|
|
|
2254
2331
|
/** Set to stop medals from being unlockable
|
|
2255
2332
|
* @param {boolean} preventUnlock
|
|
@@ -2289,6 +2366,7 @@ function setDebugKey(key) { debugKey = key; }
|
|
|
2289
2366
|
* - Collision for objects can be set to be solid to block other objects
|
|
2290
2367
|
* - Objects may get pushed into overlapping other solid objects, if so they will push away
|
|
2291
2368
|
* - Solid objects are more performance intensive and should be used sparingly
|
|
2369
|
+
* @memberof Engine
|
|
2292
2370
|
* @example
|
|
2293
2371
|
* // create an engine object, normally you would first extend the class with your own
|
|
2294
2372
|
* const pos = vec2(2,3);
|
|
@@ -2297,18 +2375,18 @@ function setDebugKey(key) { debugKey = key; }
|
|
|
2297
2375
|
class EngineObject
|
|
2298
2376
|
{
|
|
2299
2377
|
/** Create an engine object and adds it to the list of objects
|
|
2300
|
-
* @param {Vector2} [pos=(0,0)]
|
|
2301
|
-
* @param {Vector2} [size=(1,1)]
|
|
2302
|
-
* @param {TileInfo} [tileInfo]
|
|
2303
|
-
* @param {number} [angle]
|
|
2304
|
-
* @param {Color} [color=
|
|
2305
|
-
* @param {number} [renderOrder]
|
|
2378
|
+
* @param {Vector2} [pos=(0,0)] - World space position of the object
|
|
2379
|
+
* @param {Vector2} [size=(1,1)] - World space size of the object
|
|
2380
|
+
* @param {TileInfo} [tileInfo] - Tile info to render object (undefined is untextured)
|
|
2381
|
+
* @param {number} [angle] - Angle the object is rotated by
|
|
2382
|
+
* @param {Color} [color=WHITE] - Color to apply to tile when rendered
|
|
2383
|
+
* @param {number} [renderOrder] - Objects sorted by renderOrder before being rendered
|
|
2306
2384
|
*/
|
|
2307
|
-
constructor(pos=vec2(), size=vec2(1), tileInfo, angle=0, color=
|
|
2385
|
+
constructor(pos=vec2(), size=vec2(1), tileInfo, angle=0, color=WHITE, renderOrder=0)
|
|
2308
2386
|
{
|
|
2309
2387
|
// check passed in params
|
|
2310
|
-
ASSERT(isVector2(pos), 'object pos
|
|
2311
|
-
ASSERT(isVector2(size), 'object size
|
|
2388
|
+
ASSERT(isVector2(pos), 'object pos must be a vec2');
|
|
2389
|
+
ASSERT(isVector2(size), 'object size must be a vec2');
|
|
2312
2390
|
ASSERT(!tileInfo || tileInfo instanceof TileInfo, 'object tileInfo should be a TileInfo or undefined');
|
|
2313
2391
|
ASSERT(typeof angle === 'number' && isFinite(angle), 'object angle should be a number');
|
|
2314
2392
|
ASSERT(isColor(color), 'object color should be a valid rgba color');
|
|
@@ -2593,7 +2671,7 @@ class EngineObject
|
|
|
2593
2671
|
drawTile(this.pos, this.drawSize || this.size, this.tileInfo, this.color, this.angle, this.mirror, this.additiveColor);
|
|
2594
2672
|
}
|
|
2595
2673
|
|
|
2596
|
-
/** Destroy this object, destroy its children, detach
|
|
2674
|
+
/** Destroy this object, destroy its children, detach its parent, and mark it for removal */
|
|
2597
2675
|
destroy()
|
|
2598
2676
|
{
|
|
2599
2677
|
if (this.destroyed)
|
|
@@ -2667,6 +2745,8 @@ class EngineObject
|
|
|
2667
2745
|
addChild(child, localPos=vec2(), localAngle=0)
|
|
2668
2746
|
{
|
|
2669
2747
|
ASSERT(!child.parent && !this.children.includes(child));
|
|
2748
|
+
ASSERT(child instanceof EngineObject, 'child must be an EngineObject');
|
|
2749
|
+
ASSERT(child !== this, 'cannot add self as child');
|
|
2670
2750
|
this.children.push(child);
|
|
2671
2751
|
child.parent = this;
|
|
2672
2752
|
child.localPos = localPos.copy();
|
|
@@ -2678,6 +2758,7 @@ class EngineObject
|
|
|
2678
2758
|
removeChild(child)
|
|
2679
2759
|
{
|
|
2680
2760
|
ASSERT(child.parent === this && this.children.includes(child));
|
|
2761
|
+
ASSERT(child instanceof EngineObject, 'child must be an EngineObject');
|
|
2681
2762
|
this.children.splice(this.children.indexOf(child), 1);
|
|
2682
2763
|
child.parent = 0;
|
|
2683
2764
|
}
|
|
@@ -2816,7 +2897,7 @@ let drawCount;
|
|
|
2816
2897
|
* Create a tile info object using a grid based system
|
|
2817
2898
|
* - This can take vecs or floats for easier use and conversion
|
|
2818
2899
|
* - If an index is passed in, the tile size and index will determine the position
|
|
2819
|
-
* @param {Vector2|number} [pos=0] -
|
|
2900
|
+
* @param {Vector2|number} [pos=0] - Position of the tile in pixels, or tile index
|
|
2820
2901
|
* @param {Vector2|number} [size=tileSizeDefault] - Size of tile in pixels
|
|
2821
2902
|
* @param {number} [textureIndex] - Texture index to use
|
|
2822
2903
|
* @param {number} [padding] - How many pixels padding around tiles
|
|
@@ -2861,6 +2942,7 @@ function tile(pos=new Vector2, size=tileSizeDefault, textureIndex=0, padding=0)
|
|
|
2861
2942
|
|
|
2862
2943
|
/**
|
|
2863
2944
|
* Tile Info - Stores info about how to draw a tile
|
|
2945
|
+
* @memberof Draw
|
|
2864
2946
|
*/
|
|
2865
2947
|
class TileInfo
|
|
2866
2948
|
{
|
|
@@ -2921,7 +3003,10 @@ class TileInfo
|
|
|
2921
3003
|
}
|
|
2922
3004
|
}
|
|
2923
3005
|
|
|
2924
|
-
/**
|
|
3006
|
+
/**
|
|
3007
|
+
* Tile Info - Stores info about each texture
|
|
3008
|
+
* @memberof Draw
|
|
3009
|
+
*/
|
|
2925
3010
|
class TextureInfo
|
|
2926
3011
|
{
|
|
2927
3012
|
/**
|
|
@@ -2967,10 +3052,11 @@ class TextureInfo
|
|
|
2967
3052
|
function drawTile(pos, size=new Vector2(1), tileInfo, color=WHITE,
|
|
2968
3053
|
angle=0, mirror, additiveColor, useWebGL=glEnable, screenSpace, context)
|
|
2969
3054
|
{
|
|
2970
|
-
ASSERT(isVector2(pos), '
|
|
2971
|
-
ASSERT(isVector2(size), '
|
|
2972
|
-
ASSERT(isColor(color)
|
|
2973
|
-
ASSERT(isNumber(angle), '
|
|
3055
|
+
ASSERT(isVector2(pos), 'pos must be a vec2');
|
|
3056
|
+
ASSERT(isVector2(size), 'size must be a vec2');
|
|
3057
|
+
ASSERT(isColor(color), 'color is invalid');
|
|
3058
|
+
ASSERT(isNumber(angle), 'angle must be a number');
|
|
3059
|
+
ASSERT(!additiveColor || isColor(additiveColor), 'additiveColor must be a color');
|
|
2974
3060
|
ASSERT(!context || !useWebGL, 'context only supported in canvas 2D mode');
|
|
2975
3061
|
|
|
2976
3062
|
const textureInfo = tileInfo && tileInfo.textureInfo;
|
|
@@ -3065,10 +3151,10 @@ function drawRect(pos, size, color, angle, useWebGL, screenSpace, context)
|
|
|
3065
3151
|
* @memberof Draw */
|
|
3066
3152
|
function drawRectGradient(pos, size, colorTop=WHITE, colorBottom=BLACK, angle=0, useWebGL=glEnable, screenSpace=false, context)
|
|
3067
3153
|
{
|
|
3068
|
-
ASSERT(isVector2(pos), '
|
|
3069
|
-
ASSERT(isVector2(size), '
|
|
3070
|
-
ASSERT(isColor(colorTop) && isColor(colorBottom), '
|
|
3071
|
-
ASSERT(isNumber(angle), '
|
|
3154
|
+
ASSERT(isVector2(pos), 'pos must be a vec2');
|
|
3155
|
+
ASSERT(isVector2(size), 'size must be a vec2');
|
|
3156
|
+
ASSERT(isColor(colorTop) && isColor(colorBottom), 'color is invalid');
|
|
3157
|
+
ASSERT(isNumber(angle), 'angle must be a number');
|
|
3072
3158
|
ASSERT(!context || !useWebGL, 'context only supported in canvas 2D mode');
|
|
3073
3159
|
if (useWebGL)
|
|
3074
3160
|
{
|
|
@@ -3126,11 +3212,11 @@ function drawRectGradient(pos, size, colorTop=WHITE, colorBottom=BLACK, angle=0,
|
|
|
3126
3212
|
* @memberof Draw */
|
|
3127
3213
|
function drawLineList(points, width=.1, color, wrap=false, pos=vec2(), angle=0, useWebGL=glEnable, screenSpace, context)
|
|
3128
3214
|
{
|
|
3129
|
-
ASSERT(Array.isArray(points), '
|
|
3130
|
-
ASSERT(isNumber(width), '
|
|
3131
|
-
ASSERT(isColor(color), '
|
|
3132
|
-
ASSERT(isVector2(pos), '
|
|
3133
|
-
ASSERT(isNumber(angle), '
|
|
3215
|
+
ASSERT(Array.isArray(points), 'points must be an array');
|
|
3216
|
+
ASSERT(isNumber(width), 'width must be a number');
|
|
3217
|
+
ASSERT(isColor(color), 'color is invalid');
|
|
3218
|
+
ASSERT(isVector2(pos), 'pos must be a vec2');
|
|
3219
|
+
ASSERT(isNumber(angle), 'angle must be a number');
|
|
3134
3220
|
ASSERT(!context || !useWebGL, 'context only supported in canvas 2D mode');
|
|
3135
3221
|
if (useWebGL)
|
|
3136
3222
|
{
|
|
@@ -3201,8 +3287,8 @@ function drawLine(posA, posB, width=.1, color, pos=vec2(), angle=0, useWebGL, sc
|
|
|
3201
3287
|
* @memberof Draw */
|
|
3202
3288
|
function drawRegularPoly(pos, size=vec2(1), sides=3, color=WHITE, lineWidth=0, lineColor=BLACK, angle=0, useWebGL=glEnable, screenSpace=false, context)
|
|
3203
3289
|
{
|
|
3204
|
-
ASSERT(isVector2(size), '
|
|
3205
|
-
ASSERT(isNumber(sides), '
|
|
3290
|
+
ASSERT(isVector2(size), 'size must be a vec2');
|
|
3291
|
+
ASSERT(isNumber(sides), 'sides must be a number');
|
|
3206
3292
|
|
|
3207
3293
|
// build regular polygon points
|
|
3208
3294
|
const points = [];
|
|
@@ -3228,12 +3314,13 @@ function drawRegularPoly(pos, size=vec2(1), sides=3, color=WHITE, lineWidth=0, l
|
|
|
3228
3314
|
* @memberof Draw */
|
|
3229
3315
|
function drawPoly(points, color=WHITE, lineWidth=0, lineColor=BLACK, pos=vec2(), angle=0, useWebGL=glEnable, screenSpace=false, context=undefined)
|
|
3230
3316
|
{
|
|
3231
|
-
ASSERT(isVector2(pos), '
|
|
3232
|
-
ASSERT(Array.isArray(points), '
|
|
3233
|
-
ASSERT(isColor(color) && isColor(lineColor), '
|
|
3234
|
-
ASSERT(isNumber(lineWidth), '
|
|
3235
|
-
ASSERT(isNumber(angle), '
|
|
3317
|
+
ASSERT(isVector2(pos), 'pos must be a vec2');
|
|
3318
|
+
ASSERT(Array.isArray(points), 'points must be an array');
|
|
3319
|
+
ASSERT(isColor(color) && isColor(lineColor), 'color is invalid');
|
|
3320
|
+
ASSERT(isNumber(lineWidth), 'lineWidth must be a number');
|
|
3321
|
+
ASSERT(isNumber(angle), 'angle must be a number');
|
|
3236
3322
|
ASSERT(!context || !useWebGL, 'context only supported in canvas 2D mode');
|
|
3323
|
+
|
|
3237
3324
|
if (useWebGL)
|
|
3238
3325
|
{
|
|
3239
3326
|
let scale = 1;
|
|
@@ -3280,13 +3367,14 @@ function drawPoly(points, color=WHITE, lineWidth=0, lineColor=BLACK, pos=vec2(),
|
|
|
3280
3367
|
* @memberof Draw */
|
|
3281
3368
|
function drawEllipse(pos, size=vec2(1), color=WHITE, angle=0, lineWidth=0, lineColor=BLACK, useWebGL=glEnable, screenSpace=false, context)
|
|
3282
3369
|
{
|
|
3283
|
-
ASSERT(isVector2(pos), '
|
|
3284
|
-
ASSERT(isVector2(size), '
|
|
3285
|
-
ASSERT(isColor(color) && isColor(lineColor), '
|
|
3286
|
-
ASSERT(isNumber(angle), '
|
|
3287
|
-
ASSERT(isNumber(lineWidth), '
|
|
3288
|
-
ASSERT(lineWidth >= 0 && lineWidth < size.x && lineWidth < size.y, '
|
|
3370
|
+
ASSERT(isVector2(pos), 'pos must be a vec2');
|
|
3371
|
+
ASSERT(isVector2(size), 'size must be a vec2');
|
|
3372
|
+
ASSERT(isColor(color) && isColor(lineColor), 'color is invalid');
|
|
3373
|
+
ASSERT(isNumber(angle), 'angle must be a number');
|
|
3374
|
+
ASSERT(isNumber(lineWidth), 'lineWidth must be a number');
|
|
3375
|
+
ASSERT(lineWidth >= 0 && lineWidth < size.x && lineWidth < size.y, 'invalid lineWidth');
|
|
3289
3376
|
ASSERT(!context || !useWebGL, 'context only supported in canvas 2D mode');
|
|
3377
|
+
|
|
3290
3378
|
if (useWebGL)
|
|
3291
3379
|
{
|
|
3292
3380
|
// draw as a regular polygon
|
|
@@ -3323,21 +3411,32 @@ function drawEllipse(pos, size=vec2(1), color=WHITE, angle=0, lineWidth=0, lineC
|
|
|
3323
3411
|
* @memberof Draw */
|
|
3324
3412
|
function drawCircle(pos, size=1, color=WHITE, lineWidth=0, lineColor=BLACK, useWebGL=glEnable, screenSpace=false, context)
|
|
3325
3413
|
{
|
|
3326
|
-
ASSERT(isNumber(size), '
|
|
3414
|
+
ASSERT(isNumber(size), 'size must be a number');
|
|
3327
3415
|
drawEllipse(pos, vec2(size), color, 0, lineWidth, lineColor, useWebGL, screenSpace, context);
|
|
3328
3416
|
}
|
|
3329
3417
|
|
|
3418
|
+
/**
|
|
3419
|
+
* @callback Canvas2DDrawFunction - A function that draws to a 2D canvas context
|
|
3420
|
+
* @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} context
|
|
3421
|
+
* @memberof Draw
|
|
3422
|
+
*/
|
|
3423
|
+
|
|
3330
3424
|
/** Draw directly to a 2d canvas context in world space
|
|
3331
3425
|
* @param {Vector2} pos
|
|
3332
3426
|
* @param {Vector2} size
|
|
3333
3427
|
* @param {number} angle
|
|
3334
3428
|
* @param {boolean} [mirror]
|
|
3335
|
-
* @param {
|
|
3429
|
+
* @param {Canvas2DDrawFunction} [drawFunction]
|
|
3336
3430
|
* @param {boolean} [screenSpace=false]
|
|
3337
3431
|
* @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} [context=drawContext]
|
|
3338
3432
|
* @memberof Draw */
|
|
3339
3433
|
function drawCanvas2D(pos, size, angle=0, mirror=false, drawFunction, screenSpace=false, context=drawContext)
|
|
3340
3434
|
{
|
|
3435
|
+
ASSERT(isVector2(pos), 'pos must be a vec2');
|
|
3436
|
+
ASSERT(isVector2(size), 'size must be a vec2');
|
|
3437
|
+
ASSERT(isNumber(angle), 'angle must be a number');
|
|
3438
|
+
ASSERT(typeof drawFunction === 'function', 'drawFunction must be a function');
|
|
3439
|
+
|
|
3341
3440
|
if (!screenSpace)
|
|
3342
3441
|
{
|
|
3343
3442
|
// transform from world space to screen space
|
|
@@ -3405,6 +3504,16 @@ function drawTextOverlay(text, pos, size=1, color, lineWidth=0, lineColor, textA
|
|
|
3405
3504
|
* @memberof Draw */
|
|
3406
3505
|
function drawTextScreen(text, pos, size=1, color=WHITE, lineWidth=0, lineColor=BLACK, textAlign='center', font=fontDefault, maxWidth, context=overlayContext)
|
|
3407
3506
|
{
|
|
3507
|
+
ASSERT(isString(text), 'text must be a string');
|
|
3508
|
+
ASSERT(isVector2(pos), 'pos must be a vec2');
|
|
3509
|
+
ASSERT(isNumber(size), 'size must be a number');
|
|
3510
|
+
ASSERT(isColor(color), 'color must be a color');
|
|
3511
|
+
ASSERT(isNumber(lineWidth), 'lineWidth must be a number');
|
|
3512
|
+
ASSERT(isColor(lineColor), 'lineColor must be a color');
|
|
3513
|
+
ASSERT(isColor(lineColor), 'lineColor must be a color');
|
|
3514
|
+
ASSERT(['left','center','right'].includes(textAlign), 'align must be left, center, or right');
|
|
3515
|
+
ASSERT(isString(font), 'font must be a string');
|
|
3516
|
+
|
|
3408
3517
|
context.fillStyle = color.toString();
|
|
3409
3518
|
context.strokeStyle = lineColor.toString();
|
|
3410
3519
|
context.lineWidth = lineWidth;
|
|
@@ -3606,6 +3715,7 @@ let engineFontImage;
|
|
|
3606
3715
|
* - 96 characters (from space to tilde) are stored in an image
|
|
3607
3716
|
* - Uses a default 8x8 font if none is supplied
|
|
3608
3717
|
* - You can also use fonts from the main tile sheet
|
|
3718
|
+
* @memberof Draw
|
|
3609
3719
|
* @example
|
|
3610
3720
|
* // use built in font
|
|
3611
3721
|
* const font = new FontImage;
|
|
@@ -3662,7 +3772,7 @@ class FontImage
|
|
|
3662
3772
|
* @param {boolean} [center]
|
|
3663
3773
|
* @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} [context=drawContext]
|
|
3664
3774
|
*/
|
|
3665
|
-
drawTextScreen(text, pos, scale=4, center, context=overlayContext)
|
|
3775
|
+
drawTextScreen(text, pos, scale=4, center=true, context=overlayContext)
|
|
3666
3776
|
{
|
|
3667
3777
|
context.save();
|
|
3668
3778
|
const size = this.tileSize;
|
|
@@ -4356,6 +4466,7 @@ function audioInit()
|
|
|
4356
4466
|
* Sound Object - Stores a sound for later use and can be played positionally
|
|
4357
4467
|
*
|
|
4358
4468
|
* <a href=https://killedbyapixel.github.io/ZzFX/>Create sounds using the ZzFX Sound Designer.</a>
|
|
4469
|
+
* @memberof Audio
|
|
4359
4470
|
* @example
|
|
4360
4471
|
* // create a sound
|
|
4361
4472
|
* const sound_example = new Sound([.5,.5]);
|
|
@@ -4450,12 +4561,12 @@ class Sound
|
|
|
4450
4561
|
|
|
4451
4562
|
/** Play the sound as a musical note with a semitone offset
|
|
4452
4563
|
* This can be used to play music with chromatic scales
|
|
4453
|
-
* @param {number} semitoneOffset - How many semitones to offset pitch
|
|
4564
|
+
* @param {number} [semitoneOffset=0] - How many semitones to offset pitch
|
|
4454
4565
|
* @param {Vector2} [pos] - World space position to play the sound if any
|
|
4455
4566
|
* @param {number} [volume=1] - How much to scale volume by
|
|
4456
4567
|
* @return {SoundInstance} - The audio source node
|
|
4457
4568
|
*/
|
|
4458
|
-
playNote(semitoneOffset, pos, volume)
|
|
4569
|
+
playNote(semitoneOffset=0, pos, volume)
|
|
4459
4570
|
{
|
|
4460
4571
|
const pitch = getNoteFrequency(semitoneOffset, 1);
|
|
4461
4572
|
return this.play(pos, volume, pitch, 0);
|
|
@@ -4478,6 +4589,8 @@ class Sound
|
|
|
4478
4589
|
/**
|
|
4479
4590
|
* Sound Wave Object - Stores a wave sound for later use and can be played positionally
|
|
4480
4591
|
* - this can be used to play wave, mp3, and ogg files
|
|
4592
|
+
* @extends Sound
|
|
4593
|
+
* @memberof Audio
|
|
4481
4594
|
* @example
|
|
4482
4595
|
* // create a sound
|
|
4483
4596
|
* const sound_example = new SoundWave('sound.mp3');
|
|
@@ -4487,22 +4600,29 @@ class Sound
|
|
|
4487
4600
|
*/
|
|
4488
4601
|
class SoundWave extends Sound
|
|
4489
4602
|
{
|
|
4603
|
+
/**
|
|
4604
|
+
* @callback SoundLoadCallback - Function called when sound is loaded
|
|
4605
|
+
* @param {SoundWave} sound
|
|
4606
|
+
* @memberof Audio
|
|
4607
|
+
*/
|
|
4608
|
+
|
|
4490
4609
|
/** Create a sound object and cache the wave file for later use
|
|
4491
4610
|
* @param {string} filename - Filename of audio file to load
|
|
4492
4611
|
* @param {number} [randomness] - How much to randomize frequency each time sound plays
|
|
4493
4612
|
* @param {number} [range=soundDefaultRange] - World space max range of sound
|
|
4494
4613
|
* @param {number} [taper=soundDefaultTaper] - At what percentage of range should it start tapering
|
|
4495
|
-
* @param {
|
|
4614
|
+
* @param {SoundLoadCallback} [onloadCallback] - callback function to call when sound is loaded
|
|
4496
4615
|
*/
|
|
4497
4616
|
constructor(filename, randomness=0, range, taper, onloadCallback)
|
|
4498
4617
|
{
|
|
4499
4618
|
super(undefined, range, taper);
|
|
4500
4619
|
if (!soundEnable || headlessMode) return;
|
|
4620
|
+
ASSERT(!filename || isString(filename), 'filename must be a string');
|
|
4501
4621
|
|
|
4502
|
-
/** @property {
|
|
4622
|
+
/** @property {SoundLoadCallback} - callback function to call when sound is loaded */
|
|
4503
4623
|
this.onloadCallback = onloadCallback;
|
|
4504
4624
|
this.randomness = randomness;
|
|
4505
|
-
this.loadSound(filename);
|
|
4625
|
+
filename && this.loadSound(filename);
|
|
4506
4626
|
}
|
|
4507
4627
|
|
|
4508
4628
|
/** Loads a sound from a URL and decodes it into sample data. Must be used with await!
|
|
@@ -4511,6 +4631,8 @@ class SoundWave extends Sound
|
|
|
4511
4631
|
async loadSound(filename)
|
|
4512
4632
|
{
|
|
4513
4633
|
const response = await fetch(filename);
|
|
4634
|
+
if (!response.ok)
|
|
4635
|
+
throw new Error(`Failed to load sound from ${filename}: ${response.status} ${response.statusText}`);
|
|
4514
4636
|
const arrayBuffer = await response.arrayBuffer();
|
|
4515
4637
|
const audioBuffer = await audioContext.decodeAudioData(arrayBuffer);
|
|
4516
4638
|
|
|
@@ -4546,7 +4668,7 @@ class SoundWave extends Sound
|
|
|
4546
4668
|
this.sampleChannels = sampleChannels;
|
|
4547
4669
|
this.loadedPercent = 1;
|
|
4548
4670
|
if (this.onloadCallback)
|
|
4549
|
-
this.onloadCallback();
|
|
4671
|
+
this.onloadCallback(this);
|
|
4550
4672
|
}
|
|
4551
4673
|
}
|
|
4552
4674
|
|
|
@@ -4555,6 +4677,7 @@ class SoundWave extends Sound
|
|
|
4555
4677
|
/**
|
|
4556
4678
|
* Sound Instance - Wraps an AudioBufferSourceNode for individual sound control
|
|
4557
4679
|
* Represents a single playing instance of a sound with pause/resume capabilities
|
|
4680
|
+
* @memberof Audio
|
|
4558
4681
|
* @example
|
|
4559
4682
|
* // Play a sound and get an instance for control
|
|
4560
4683
|
* const jumpSound = new Sound([.5,.5,220]);
|
|
@@ -4754,6 +4877,12 @@ function getNoteFrequency(semitoneOffset, rootFrequency=220)
|
|
|
4754
4877
|
|
|
4755
4878
|
///////////////////////////////////////////////////////////////////////////////
|
|
4756
4879
|
|
|
4880
|
+
/**
|
|
4881
|
+
* @callback AudioEndedCallback - Function called when a sound ends
|
|
4882
|
+
* @param {AudioBufferSourceNode} source
|
|
4883
|
+
* @memberof Audio
|
|
4884
|
+
*/
|
|
4885
|
+
|
|
4757
4886
|
/** Play cached audio samples with given settings
|
|
4758
4887
|
* @param {Array} sampleChannels - Array of arrays of samples to play (for stereo playback)
|
|
4759
4888
|
* @param {number} [volume] - How much to scale volume by
|
|
@@ -4763,7 +4892,7 @@ function getNoteFrequency(semitoneOffset, rootFrequency=220)
|
|
|
4763
4892
|
* @param {number} [sampleRate=44100] - Sample rate for the sound
|
|
4764
4893
|
* @param {GainNode} [gainNode] - Optional gain node for volume control while playing
|
|
4765
4894
|
* @param {number} [offset] - Offset in seconds to start playback from
|
|
4766
|
-
* @param {
|
|
4895
|
+
* @param {AudioEndedCallback} [onended] - Callback for when the sound ends
|
|
4767
4896
|
* @return {AudioBufferSourceNode} - The audio node of the sound played
|
|
4768
4897
|
* @memberof Audio */
|
|
4769
4898
|
function playSamples(sampleChannels, volume=1, rate=1, pan=0, loop=false, sampleRate=audioDefaultSampleRate, gainNode, offset=0, onended)
|
|
@@ -4795,16 +4924,14 @@ function playSamples(sampleChannels, volume=1, rate=1, pan=0, loop=false, sample
|
|
|
4795
4924
|
if (onended)
|
|
4796
4925
|
source.addEventListener('ended', ()=> onended(source));
|
|
4797
4926
|
|
|
4927
|
+
const startOffset = offset * rate;
|
|
4798
4928
|
if (!audioIsRunning())
|
|
4799
4929
|
{
|
|
4800
|
-
// fix stalled audio
|
|
4801
|
-
audioContext.resume();
|
|
4802
|
-
return;
|
|
4930
|
+
// fix stalled audio and start
|
|
4931
|
+
audioContext.resume().then(()=>source.start(0, startOffset));
|
|
4803
4932
|
}
|
|
4804
|
-
|
|
4805
|
-
|
|
4806
|
-
const startOffset = offset * rate;
|
|
4807
|
-
source.start(0, startOffset);
|
|
4933
|
+
else
|
|
4934
|
+
source.start(0, startOffset);
|
|
4808
4935
|
return source;
|
|
4809
4936
|
}
|
|
4810
4937
|
|
|
@@ -4969,7 +5096,7 @@ function zzfxG
|
|
|
4969
5096
|
* - Unlimited numbers of layers, allocates canvases as needed
|
|
4970
5097
|
* - Tile layers can be drawn to using their context with canvas2d
|
|
4971
5098
|
* - Tile layers can also have collision with EngineObjects
|
|
4972
|
-
* @namespace
|
|
5099
|
+
* @namespace TileLayers
|
|
4973
5100
|
*/
|
|
4974
5101
|
|
|
4975
5102
|
///////////////////////////////////////////////////////////////////////////////
|
|
@@ -4977,13 +5104,13 @@ function zzfxG
|
|
|
4977
5104
|
|
|
4978
5105
|
/** Keep track of all tile layers with collision
|
|
4979
5106
|
* @type {Array<TileCollisionLayer>}
|
|
4980
|
-
* @memberof
|
|
5107
|
+
* @memberof TileLayers */
|
|
4981
5108
|
const tileCollisionLayers = [];
|
|
4982
5109
|
|
|
4983
5110
|
/** Get tile collision data for a given cell in the grid
|
|
4984
5111
|
* @param {Vector2} pos
|
|
4985
5112
|
* @return {number}
|
|
4986
|
-
* @memberof
|
|
5113
|
+
* @memberof TileLayers */
|
|
4987
5114
|
function tileCollisionGetData(pos)
|
|
4988
5115
|
{
|
|
4989
5116
|
// check all tile collision layers
|
|
@@ -4999,7 +5126,7 @@ function tileCollisionGetData(pos)
|
|
|
4999
5126
|
* @param {EngineObject} [object] - An object or undefined for generic test
|
|
5000
5127
|
* @param {boolean} [solidOnly] - Only check solid layers if true
|
|
5001
5128
|
* @return {TileCollisionLayer}
|
|
5002
|
-
* @memberof
|
|
5129
|
+
* @memberof TileLayers */
|
|
5003
5130
|
function tileCollisionTest(pos, size=vec2(), object, solidOnly=true)
|
|
5004
5131
|
{
|
|
5005
5132
|
for (const layer of tileCollisionLayers)
|
|
@@ -5017,7 +5144,7 @@ function tileCollisionTest(pos, size=vec2(), object, solidOnly=true)
|
|
|
5017
5144
|
* @param {EngineObject} [object] - An object or undefined for generic test
|
|
5018
5145
|
* @param {boolean} [solidOnly=true] - Only check solid layers if true
|
|
5019
5146
|
* @return {Vector2}
|
|
5020
|
-
* @memberof
|
|
5147
|
+
* @memberof TileLayers */
|
|
5021
5148
|
function tileCollisionRaycast(posStart, posEnd, object, solidOnly=true)
|
|
5022
5149
|
{
|
|
5023
5150
|
for (const layer of tileCollisionLayers)
|
|
@@ -5040,8 +5167,8 @@ function tileCollisionRaycast(posStart, posEnd, object, solidOnly=true)
|
|
|
5040
5167
|
* @param {number} [collisionLayer] - Layer to use for collision if any
|
|
5041
5168
|
* @param {boolean} [draw] - Should the layer be drawn automatically
|
|
5042
5169
|
* @return {Array<TileCollisionLayer>}
|
|
5043
|
-
* @memberof
|
|
5044
|
-
function
|
|
5170
|
+
* @memberof TileLayers */
|
|
5171
|
+
function tileLayersLoad(tileMapData, tileInfo=tile(), renderOrder=0, collisionLayer, draw=true)
|
|
5045
5172
|
{
|
|
5046
5173
|
if (!tileMapData)
|
|
5047
5174
|
{
|
|
@@ -5071,6 +5198,12 @@ function tileCollisionLoad(tileMapData, tileInfo=tile(), renderOrder=0, collisio
|
|
|
5071
5198
|
const tileLayer = new TileCollisionLayer(vec2(), levelSize, tileInfo, layerRenderOrder);
|
|
5072
5199
|
tileLayers[layerIndex] = tileLayer;
|
|
5073
5200
|
|
|
5201
|
+
// apply layer color
|
|
5202
|
+
const layerColor = dataLayer.tintcolor ?
|
|
5203
|
+
new Color().setHex(dataLayer.tintcolor) :
|
|
5204
|
+
dataLayer.color || WHITE;
|
|
5205
|
+
ASSERT(isColor(layerColor), 'layer color is not a color');
|
|
5206
|
+
|
|
5074
5207
|
for (let x=levelSize.x; x--;)
|
|
5075
5208
|
for (let y=levelSize.y; y--;)
|
|
5076
5209
|
{
|
|
@@ -5078,7 +5211,7 @@ function tileCollisionLoad(tileMapData, tileInfo=tile(), renderOrder=0, collisio
|
|
|
5078
5211
|
const data = dataLayer.data[x + y*levelSize.x];
|
|
5079
5212
|
if (data)
|
|
5080
5213
|
{
|
|
5081
|
-
const layerData = new TileLayerData(data-1);
|
|
5214
|
+
const layerData = new TileLayerData(data-1, 0, false, layerColor);
|
|
5082
5215
|
tileLayer.setData(pos, layerData);
|
|
5083
5216
|
|
|
5084
5217
|
// set collision for top layer
|
|
@@ -5095,6 +5228,7 @@ function tileCollisionLoad(tileMapData, tileInfo=tile(), renderOrder=0, collisio
|
|
|
5095
5228
|
///////////////////////////////////////////////////////////////////////////////
|
|
5096
5229
|
/**
|
|
5097
5230
|
* Tile layer data object stores info about how to draw a tile
|
|
5231
|
+
* @memberof TileLayers
|
|
5098
5232
|
* @example
|
|
5099
5233
|
* // create tile layer data with tile index 0 and random orientation and color
|
|
5100
5234
|
* const tileIndex = 0;
|
|
@@ -5132,6 +5266,7 @@ class TileLayerData
|
|
|
5132
5266
|
* - Contains an offscreen canvas that can be rendered to
|
|
5133
5267
|
* - WebGL rendering is optional, call useWebGL to enable
|
|
5134
5268
|
* @extends EngineObject
|
|
5269
|
+
* @memberof TileLayers
|
|
5135
5270
|
* @example
|
|
5136
5271
|
* const canvasLayer = new CanvasLayer(vec2(), vec2(200,100));
|
|
5137
5272
|
*/
|
|
@@ -5146,6 +5281,7 @@ class CanvasLayer extends EngineObject
|
|
|
5146
5281
|
*/
|
|
5147
5282
|
constructor(position, size, angle=0, renderOrder=0, canvasSize=vec2(512))
|
|
5148
5283
|
{
|
|
5284
|
+
ASSERT(isVector2(canvasSize), 'canvasSize must be a Vector2');
|
|
5149
5285
|
super(position, size, undefined, angle, WHITE, renderOrder);
|
|
5150
5286
|
|
|
5151
5287
|
/** @property {HTMLCanvasElement} - The canvas used by this layer */
|
|
@@ -5193,12 +5329,18 @@ class CanvasLayer extends EngineObject
|
|
|
5193
5329
|
drawTile(pos, size, tileInfo, color, angle, mirror, additiveColor, useWebGL, screenSpace, context);
|
|
5194
5330
|
}
|
|
5195
5331
|
|
|
5332
|
+
/**
|
|
5333
|
+
* @callback Canvas2DDrawCallback - Function that draws to a canvas 2D context
|
|
5334
|
+
* @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} context
|
|
5335
|
+
* @memberof TileLayers
|
|
5336
|
+
*/
|
|
5337
|
+
|
|
5196
5338
|
/** Draw onto the layer canvas in world space (bypass WebGL)
|
|
5197
5339
|
* @param {Vector2} pos
|
|
5198
5340
|
* @param {Vector2} size
|
|
5199
5341
|
* @param {number} angle
|
|
5200
5342
|
* @param {boolean} mirror
|
|
5201
|
-
* @param {
|
|
5343
|
+
* @param {Canvas2DDrawCallback} drawFunction */
|
|
5202
5344
|
drawCanvas2D(pos, size, angle, mirror, drawFunction)
|
|
5203
5345
|
{
|
|
5204
5346
|
const context = this.context;
|
|
@@ -5235,7 +5377,7 @@ class CanvasLayer extends EngineObject
|
|
|
5235
5377
|
else
|
|
5236
5378
|
{
|
|
5237
5379
|
// untextured
|
|
5238
|
-
context.fillStyle = color;
|
|
5380
|
+
context.fillStyle = color.toString();
|
|
5239
5381
|
context.fillRect(-.5, -.5, 1, 1);
|
|
5240
5382
|
}
|
|
5241
5383
|
});
|
|
@@ -5272,7 +5414,9 @@ class CanvasLayer extends EngineObject
|
|
|
5272
5414
|
* - To allow dynamic modifications, layers are rendered using canvas 2d
|
|
5273
5415
|
* - Some devices like mobile phones are limited to 4k texture resolution
|
|
5274
5416
|
* - For with 16x16 tiles this limits layers to 256x256 on mobile devices
|
|
5417
|
+
* - Tile layers are centered on their corner, so normal levels are at (0,0)
|
|
5275
5418
|
* @extends CanvasLayer
|
|
5419
|
+
* @memberof TileLayers
|
|
5276
5420
|
* @example
|
|
5277
5421
|
* const tileLayer = new TileLayer(vec2(), vec2(200,100));
|
|
5278
5422
|
*/
|
|
@@ -5282,15 +5426,14 @@ class TileLayer extends CanvasLayer
|
|
|
5282
5426
|
* @param {Vector2} position - World space position
|
|
5283
5427
|
* @param {Vector2} size - World space size
|
|
5284
5428
|
* @param {TileInfo} [tileInfo] - Default tile info for layer (used for size and texture)
|
|
5285
|
-
* @param {Vector2} [scale=(1,1)] - How much to scale this layer when rendered
|
|
5286
5429
|
* @param {number} [renderOrder] - Objects are sorted by renderOrder
|
|
5287
5430
|
* @param {boolean} [useWebGL=glEnable] - Use accelerated WebGL rendering
|
|
5288
5431
|
*/
|
|
5289
|
-
constructor(position, size, tileInfo=tile(),
|
|
5432
|
+
constructor(position, size, tileInfo=tile(), renderOrder=0, useWebGL=glEnable)
|
|
5290
5433
|
{
|
|
5291
5434
|
super(position, size, 0, renderOrder, size);
|
|
5435
|
+
|
|
5292
5436
|
this.tileInfo = tileInfo;
|
|
5293
|
-
|
|
5294
5437
|
const canvasSize = size.multiply(tileInfo.size);
|
|
5295
5438
|
/** @property {HTMLCanvasElement} - The canvas used by this tile layer */
|
|
5296
5439
|
this.canvas = new OffscreenCanvas(canvasSize.x, canvasSize.y);
|
|
@@ -5327,6 +5470,8 @@ class TileLayer extends CanvasLayer
|
|
|
5327
5470
|
* @param {boolean} [redraw] - Force the tile to redraw if true */
|
|
5328
5471
|
setData(layerPos, data, redraw=false)
|
|
5329
5472
|
{
|
|
5473
|
+
ASSERT(isVector2(layerPos), 'layerPos must be a Vector2');
|
|
5474
|
+
ASSERT(data instanceof TileLayerData, 'data must be a TileLayerData');
|
|
5330
5475
|
if (layerPos.arrayCheck(this.size))
|
|
5331
5476
|
{
|
|
5332
5477
|
this.data[(layerPos.y|0)*this.size.x+layerPos.x|0] = data;
|
|
@@ -5338,7 +5483,10 @@ class TileLayer extends CanvasLayer
|
|
|
5338
5483
|
* @param {Vector2} layerPos - Local position in array
|
|
5339
5484
|
* @return {TileLayerData} */
|
|
5340
5485
|
getData(layerPos)
|
|
5341
|
-
{
|
|
5486
|
+
{
|
|
5487
|
+
ASSERT(isVector2(layerPos), 'layerPos must be a Vector2');
|
|
5488
|
+
return layerPos.arrayCheck(this.size) && this.data[(layerPos.y|0)*this.size.x+layerPos.x|0];
|
|
5489
|
+
}
|
|
5342
5490
|
|
|
5343
5491
|
// Render the tile layer, called automatically by the engine
|
|
5344
5492
|
render()
|
|
@@ -5347,9 +5495,10 @@ class TileLayer extends CanvasLayer
|
|
|
5347
5495
|
|
|
5348
5496
|
// draw the tile layer as a single tile
|
|
5349
5497
|
const tileInfo = new TileInfo().setFullImage(this.canvas, this.glTexture);
|
|
5350
|
-
const
|
|
5498
|
+
const size = this.drawSize || this.size;
|
|
5499
|
+
const pos = this.pos.add(size.scale(.5));
|
|
5351
5500
|
const useWebGL = glEnable && this.glTexture !== undefined;
|
|
5352
|
-
drawTile(pos,
|
|
5501
|
+
drawTile(pos, size, tileInfo, WHITE, 0, false, CLEAR_BLACK, useWebGL);
|
|
5353
5502
|
}
|
|
5354
5503
|
|
|
5355
5504
|
/** Draw all the tile data to an offscreen canvas
|
|
@@ -5441,6 +5590,7 @@ class TileLayer extends CanvasLayer
|
|
|
5441
5590
|
* - there can be multiple tile collision layers
|
|
5442
5591
|
* - tile collision layers should not overlap each other
|
|
5443
5592
|
* @extends TileLayer
|
|
5593
|
+
* @memberof TileLayers
|
|
5444
5594
|
*/
|
|
5445
5595
|
class TileCollisionLayer extends TileLayer
|
|
5446
5596
|
{
|
|
@@ -5453,8 +5603,7 @@ class TileCollisionLayer extends TileLayer
|
|
|
5453
5603
|
*/
|
|
5454
5604
|
constructor(position, size, tileInfo=tile(), renderOrder=0, useWebGL=glEnable)
|
|
5455
5605
|
{
|
|
5456
|
-
|
|
5457
|
-
super(position, size.floor(), tileInfo, scale, renderOrder, useWebGL);
|
|
5606
|
+
super(position, size.floor(), tileInfo, renderOrder, useWebGL);
|
|
5458
5607
|
|
|
5459
5608
|
/** @property {Array<number>} - The tile collision grid */
|
|
5460
5609
|
this.collisionData = [];
|
|
@@ -5484,6 +5633,7 @@ class TileCollisionLayer extends TileLayer
|
|
|
5484
5633
|
* @param {Vector2} size - width and height of tile collision 2d grid */
|
|
5485
5634
|
initCollision(size)
|
|
5486
5635
|
{
|
|
5636
|
+
ASSERT(isVector2(size), 'size must be a Vector2');
|
|
5487
5637
|
this.size = size.floor();
|
|
5488
5638
|
this.collisionData = [];
|
|
5489
5639
|
this.collisionData.length = size.area();
|
|
@@ -5495,6 +5645,7 @@ class TileCollisionLayer extends TileLayer
|
|
|
5495
5645
|
* @param {number} [data] */
|
|
5496
5646
|
setCollisionData(gridPos, data=1)
|
|
5497
5647
|
{
|
|
5648
|
+
ASSERT(isVector2(gridPos), 'gridPos must be a Vector2');
|
|
5498
5649
|
const i = (gridPos.y|0)*this.size.x + gridPos.x|0;
|
|
5499
5650
|
gridPos.arrayCheck(this.size) && (this.collisionData[i] = data);
|
|
5500
5651
|
}
|
|
@@ -5504,6 +5655,7 @@ class TileCollisionLayer extends TileLayer
|
|
|
5504
5655
|
* @return {number} */
|
|
5505
5656
|
getCollisionData(gridPos)
|
|
5506
5657
|
{
|
|
5658
|
+
ASSERT(isVector2(gridPos), 'gridPos must be a Vector2');
|
|
5507
5659
|
const i = (gridPos.y|0)*this.size.x + gridPos.x|0;
|
|
5508
5660
|
return gridPos.arrayCheck(this.size) ? this.collisionData[i] : 0;
|
|
5509
5661
|
}
|
|
@@ -5515,6 +5667,9 @@ class TileCollisionLayer extends TileLayer
|
|
|
5515
5667
|
* @return {boolean} */
|
|
5516
5668
|
collisionTest(pos, size=new Vector2, object)
|
|
5517
5669
|
{
|
|
5670
|
+
ASSERT(isVector2(pos) && isVector2(size), 'pos and size must be Vector2s');
|
|
5671
|
+
ASSERT(!object || object instanceof EngineObject, 'object must be an EngineObject');
|
|
5672
|
+
|
|
5518
5673
|
// transform to local layer space
|
|
5519
5674
|
const posX = pos.x - this.pos.x;
|
|
5520
5675
|
const posY = pos.y - this.pos.y;
|
|
@@ -5545,6 +5700,9 @@ class TileCollisionLayer extends TileLayer
|
|
|
5545
5700
|
* @return {Vector2} */
|
|
5546
5701
|
collisionRaycast(posStart, posEnd, object)
|
|
5547
5702
|
{
|
|
5703
|
+
ASSERT(isVector2(posStart) && isVector2(posEnd), 'positions must be Vector2s');
|
|
5704
|
+
ASSERT(!object || object instanceof EngineObject, 'object must be an EngineObject');
|
|
5705
|
+
|
|
5548
5706
|
// transform to local layer space
|
|
5549
5707
|
const posStartX = posStart.x - this.pos.x;
|
|
5550
5708
|
const posStartY = posStart.y - this.pos.y;
|
|
@@ -5594,9 +5752,16 @@ class TileCollisionLayer extends TileLayer
|
|
|
5594
5752
|
* LittleJS Particle System
|
|
5595
5753
|
*/
|
|
5596
5754
|
|
|
5755
|
+
/**
|
|
5756
|
+
* @callback ParticleCallbackFunction - Function that processes a particle
|
|
5757
|
+
* @param {Particle} particle
|
|
5758
|
+
* @memberof Engine
|
|
5759
|
+
*/
|
|
5760
|
+
|
|
5597
5761
|
/**
|
|
5598
5762
|
* Particle Emitter - Spawns particles with the given settings
|
|
5599
5763
|
* @extends EngineObject
|
|
5764
|
+
* @memberof Engine
|
|
5600
5765
|
* @example
|
|
5601
5766
|
* // create a particle emitter
|
|
5602
5767
|
* let pos = vec2(2,3);
|
|
@@ -5621,10 +5786,10 @@ class ParticleEmitter extends EngineObject
|
|
|
5621
5786
|
* @param {number} [emitRate] - How many particles per second to spawn, does not emit if 0
|
|
5622
5787
|
* @param {number} [emitConeAngle=PI] - Local angle to apply velocity to particles from emitter
|
|
5623
5788
|
* @param {TileInfo} [tileInfo] - Tile info to render particles (undefined is untextured)
|
|
5624
|
-
* @param {Color} [colorStartA=
|
|
5625
|
-
* @param {Color} [colorStartB=
|
|
5626
|
-
* @param {Color} [colorEndA=
|
|
5627
|
-
* @param {Color} [colorEndB=
|
|
5789
|
+
* @param {Color} [colorStartA=WHITE] - Color at start of life 1, randomized between start colors
|
|
5790
|
+
* @param {Color} [colorStartB=WHITE] - Color at start of life 2, randomized between start colors
|
|
5791
|
+
* @param {Color} [colorEndA=CLEAR_WHITE] - Color at end of life 1, randomized between end colors
|
|
5792
|
+
* @param {Color} [colorEndB=CLEAR_WHITE] - Color at end of life 2, randomized between end colors
|
|
5628
5793
|
* @param {number} [particleTime] - How long particles live
|
|
5629
5794
|
* @param {number} [sizeStart] - How big are particles at start
|
|
5630
5795
|
* @param {number} [sizeEnd] - How big are particles at end
|
|
@@ -5651,10 +5816,10 @@ class ParticleEmitter extends EngineObject
|
|
|
5651
5816
|
emitRate = 100,
|
|
5652
5817
|
emitConeAngle = PI,
|
|
5653
5818
|
tileInfo,
|
|
5654
|
-
colorStartA =
|
|
5655
|
-
colorStartB =
|
|
5656
|
-
colorEndA =
|
|
5657
|
-
colorEndB =
|
|
5819
|
+
colorStartA = WHITE,
|
|
5820
|
+
colorStartB = WHITE,
|
|
5821
|
+
colorEndA = CLEAR_WHITE,
|
|
5822
|
+
colorEndB = CLEAR_WHITE,
|
|
5658
5823
|
particleTime = .5,
|
|
5659
5824
|
sizeStart = .1,
|
|
5660
5825
|
sizeEnd = 1,
|
|
@@ -5677,7 +5842,8 @@ class ParticleEmitter extends EngineObject
|
|
|
5677
5842
|
|
|
5678
5843
|
// emitter settings
|
|
5679
5844
|
/** @property {number|Vector2} - World space size of the emitter (float for circle diameter, vec2 for rect) */
|
|
5680
|
-
this.emitSize = emitSize
|
|
5845
|
+
this.emitSize = emitSize instanceof Vector2 ?
|
|
5846
|
+
emitSize.copy() : emitSize;
|
|
5681
5847
|
/** @property {number} - How long to stay alive (0 is forever) */
|
|
5682
5848
|
this.emitTime = emitTime;
|
|
5683
5849
|
/** @property {number} - How many particles per second to spawn, does not emit if 0 */
|
|
@@ -5687,13 +5853,13 @@ class ParticleEmitter extends EngineObject
|
|
|
5687
5853
|
|
|
5688
5854
|
// color settings
|
|
5689
5855
|
/** @property {Color} - Color at start of life 1, randomized between start colors */
|
|
5690
|
-
this.colorStartA = colorStartA;
|
|
5856
|
+
this.colorStartA = colorStartA.copy();
|
|
5691
5857
|
/** @property {Color} - Color at start of life 2, randomized between start colors */
|
|
5692
|
-
this.colorStartB = colorStartB;
|
|
5858
|
+
this.colorStartB = colorStartB.copy();
|
|
5693
5859
|
/** @property {Color} - Color at end of life 1, randomized between end colors */
|
|
5694
|
-
this.colorEndA = colorEndA;
|
|
5860
|
+
this.colorEndA = colorEndA.copy();
|
|
5695
5861
|
/** @property {Color} - Color at end of life 2, randomized between end colors */
|
|
5696
|
-
this.colorEndB = colorEndB;
|
|
5862
|
+
this.colorEndB = colorEndB.copy();
|
|
5697
5863
|
/** @property {boolean} - Should color be randomized linearly or across each component */
|
|
5698
5864
|
this.randomColorLinear = randomColorLinear;
|
|
5699
5865
|
|
|
@@ -5728,9 +5894,9 @@ class ParticleEmitter extends EngineObject
|
|
|
5728
5894
|
this.localSpace = localSpace;
|
|
5729
5895
|
/** @property {number} - If non zero the particle is drawn as a trail, stretched in the direction of velocity */
|
|
5730
5896
|
this.trailScale = 0;
|
|
5731
|
-
/** @property {
|
|
5897
|
+
/** @property {ParticleCallbackFunction} - Callback when particle is destroyed */
|
|
5732
5898
|
this.particleDestroyCallback = undefined;
|
|
5733
|
-
/** @property {
|
|
5899
|
+
/** @property {ParticleCallbackFunction} - Callback when particle is created */
|
|
5734
5900
|
this.particleCreateCallback = undefined;
|
|
5735
5901
|
/** @property {number} - Track particle emit time */
|
|
5736
5902
|
this.emitTimeBuffer = 0;
|
|
@@ -5831,6 +5997,7 @@ class ParticleEmitter extends EngineObject
|
|
|
5831
5997
|
/**
|
|
5832
5998
|
* Particle Object - Created automatically by Particle Emitters
|
|
5833
5999
|
* @extends EngineObject
|
|
6000
|
+
* @memberof Engine
|
|
5834
6001
|
*/
|
|
5835
6002
|
class Particle extends EngineObject
|
|
5836
6003
|
{
|
|
@@ -5849,7 +6016,7 @@ class Particle extends EngineObject
|
|
|
5849
6016
|
* @param {boolean} additive - Does it use additive blend mode
|
|
5850
6017
|
* @param {number} trailScale - If a trail, how long to make it
|
|
5851
6018
|
* @param {ParticleEmitter} [localSpaceEmitter] - Parent emitter if local space
|
|
5852
|
-
* @param {
|
|
6019
|
+
* @param {ParticleCallbackFunction} [destroyCallback] - Callback when particle dies
|
|
5853
6020
|
*/
|
|
5854
6021
|
constructor(position, tileInfo, angle, colorStart, colorEnd, lifeTime, sizeStart, sizeEnd, fadeRate, additive, trailScale, localSpaceEmitter, destroyCallback
|
|
5855
6022
|
)
|
|
@@ -5858,14 +6025,14 @@ class Particle extends EngineObject
|
|
|
5858
6025
|
|
|
5859
6026
|
/** @property {Color} - Color at start of life */
|
|
5860
6027
|
this.colorStart = colorStart;
|
|
5861
|
-
/** @property {Color} -
|
|
5862
|
-
this.
|
|
6028
|
+
/** @property {Color} - Color at end of life */
|
|
6029
|
+
this.colorEnd = colorEnd;
|
|
5863
6030
|
/** @property {number} - How long to live for */
|
|
5864
6031
|
this.lifeTime = lifeTime;
|
|
5865
6032
|
/** @property {number} - Size at start of life */
|
|
5866
6033
|
this.sizeStart = sizeStart;
|
|
5867
|
-
/** @property {number} -
|
|
5868
|
-
this.
|
|
6034
|
+
/** @property {number} - Size at end of life */
|
|
6035
|
+
this.sizeEnd = sizeEnd;
|
|
5869
6036
|
/** @property {number} - How quick to fade in/out */
|
|
5870
6037
|
this.fadeRate = fadeRate;
|
|
5871
6038
|
/** @property {boolean} - Is it additive */
|
|
@@ -5874,7 +6041,7 @@ class Particle extends EngineObject
|
|
|
5874
6041
|
this.trailScale = trailScale;
|
|
5875
6042
|
/** @property {ParticleEmitter} - Parent emitter if local space */
|
|
5876
6043
|
this.localSpaceEmitter = localSpaceEmitter;
|
|
5877
|
-
/** @property {
|
|
6044
|
+
/** @property {ParticleCallbackFunction} - Called when particle dies */
|
|
5878
6045
|
this.destroyCallback = destroyCallback;
|
|
5879
6046
|
// particles do not clamp speed by default
|
|
5880
6047
|
this.clampSpeed = false;
|
|
@@ -5901,54 +6068,58 @@ class Particle extends EngineObject
|
|
|
5901
6068
|
/** Render the particle, automatically called each frame, sorted by renderOrder */
|
|
5902
6069
|
render()
|
|
5903
6070
|
{
|
|
5904
|
-
//
|
|
5905
|
-
const
|
|
5906
|
-
const radius = this.sizeStart +
|
|
5907
|
-
|
|
6071
|
+
// lerp color and size
|
|
6072
|
+
const p1 = this.lifeTime > 0 ? min((time - this.spawnTime) / this.lifeTime, 1) : 1, p2 = 1-p1;
|
|
6073
|
+
const radius = p2 * this.sizeStart + p1 * this.sizeEnd;
|
|
6074
|
+
this.size.x = this.size.y = radius;
|
|
6075
|
+
this.color.r = p2 * this.colorStart.r + p1 * this.colorEnd.r;
|
|
6076
|
+
this.color.g = p2 * this.colorStart.g + p1 * this.colorEnd.g;
|
|
6077
|
+
this.color.b = p2 * this.colorStart.b + p1 * this.colorEnd.b;
|
|
6078
|
+
this.color.a = p2 * this.colorStart.a + p1 * this.colorEnd.a;
|
|
6079
|
+
|
|
6080
|
+
// fade alpha
|
|
5908
6081
|
const fadeRate = this.fadeRate/2;
|
|
5909
|
-
|
|
5910
|
-
|
|
5911
|
-
this.colorStart.g + p * this.colorEndDelta.g,
|
|
5912
|
-
this.colorStart.b + p * this.colorEndDelta.b,
|
|
5913
|
-
(this.colorStart.a + p * this.colorEndDelta.a) *
|
|
5914
|
-
(p < fadeRate ? p/fadeRate : p > 1-fadeRate ? (1-p)/fadeRate : 1)); // fade alpha
|
|
6082
|
+
this.color.a *= p1 < fadeRate ? p1/fadeRate :
|
|
6083
|
+
p1 > 1-fadeRate ? (1-p1)/fadeRate : 1;
|
|
5915
6084
|
|
|
5916
6085
|
// draw the particle
|
|
5917
6086
|
this.additive && setBlendMode(true);
|
|
5918
6087
|
|
|
6088
|
+
// update the position and angle for drawing
|
|
5919
6089
|
let pos = this.pos, angle = this.angle;
|
|
5920
6090
|
if (this.localSpaceEmitter)
|
|
5921
6091
|
{
|
|
5922
6092
|
// in local space of emitter
|
|
5923
|
-
|
|
6093
|
+
const a = this.localSpaceEmitter.angle;
|
|
6094
|
+
const c = Math.cos(a), s = Math.sin(a);
|
|
6095
|
+
pos = this.localSpaceEmitter.pos.add(
|
|
6096
|
+
new Vector2(pos.x*c - pos.y*s, pos.x*s + pos.y*c));
|
|
5924
6097
|
angle += this.localSpaceEmitter.angle;
|
|
5925
6098
|
}
|
|
5926
6099
|
if (this.trailScale)
|
|
5927
6100
|
{
|
|
5928
6101
|
// trail style particles
|
|
5929
|
-
|
|
5930
|
-
|
|
5931
|
-
|
|
5932
|
-
const speed =
|
|
6102
|
+
const direction = this.localSpaceEmitter ?
|
|
6103
|
+
this.velocity.rotate(-this.localSpaceEmitter.angle) :
|
|
6104
|
+
this.velocity;
|
|
6105
|
+
const speed = direction.length();
|
|
5933
6106
|
if (speed)
|
|
5934
6107
|
{
|
|
5935
|
-
|
|
6108
|
+
// stretch in direction of motion
|
|
5936
6109
|
const trailLength = speed * this.trailScale;
|
|
5937
|
-
size.y = max(size.x, trailLength);
|
|
5938
|
-
angle = direction.
|
|
5939
|
-
drawTile(pos
|
|
6110
|
+
this.size.y = max(this.size.x, trailLength);
|
|
6111
|
+
angle = Math.atan2(direction.x, direction.y);
|
|
6112
|
+
drawTile(pos, this.size, this.tileInfo, this.color, angle, this.mirror);
|
|
5940
6113
|
}
|
|
5941
6114
|
}
|
|
5942
6115
|
else
|
|
5943
|
-
drawTile(pos, size, this.tileInfo, color, angle, this.mirror);
|
|
6116
|
+
drawTile(pos, this.size, this.tileInfo, this.color, angle, this.mirror);
|
|
5944
6117
|
this.additive && setBlendMode();
|
|
5945
|
-
debugParticles && debugRect(pos, size, '#f005', 0, angle);
|
|
6118
|
+
debugParticles && debugRect(pos, this.size, '#f005', 0, angle);
|
|
5946
6119
|
|
|
5947
|
-
if (
|
|
6120
|
+
if (p1 === 1)
|
|
5948
6121
|
{
|
|
5949
|
-
// destroy particle when
|
|
5950
|
-
this.color = color;
|
|
5951
|
-
this.size = size;
|
|
6122
|
+
// destroy particle when its time runs out
|
|
5952
6123
|
this.destroyCallback && this.destroyCallback(this);
|
|
5953
6124
|
this.destroyed = 1;
|
|
5954
6125
|
}
|
|
@@ -6013,8 +6184,14 @@ function medalsInit(saveName)
|
|
|
6013
6184
|
}
|
|
6014
6185
|
}
|
|
6015
6186
|
|
|
6187
|
+
/**
|
|
6188
|
+
* @callback MedalCallbackFunction - Function that processes a medal
|
|
6189
|
+
* @param {Medal} medal
|
|
6190
|
+
* @memberof Medals
|
|
6191
|
+
*/
|
|
6192
|
+
|
|
6016
6193
|
/** Calls a function for each medal
|
|
6017
|
-
* @param {
|
|
6194
|
+
* @param {MedalCallbackFunction} callback
|
|
6018
6195
|
* @memberof Medals */
|
|
6019
6196
|
function medalsForEach(callback)
|
|
6020
6197
|
{ Object.values(medals).forEach(medal=>callback(medal)); }
|
|
@@ -6023,6 +6200,7 @@ function medalsForEach(callback)
|
|
|
6023
6200
|
|
|
6024
6201
|
/**
|
|
6025
6202
|
* Medal - Tracks an unlockable medal
|
|
6203
|
+
* @memberof Medals
|
|
6026
6204
|
* @example
|
|
6027
6205
|
* // create a medal
|
|
6028
6206
|
* const medal_example = new Medal(0, 'Example Medal', 'More info about the medal goes here.', '🎖️');
|
|
@@ -6091,11 +6269,12 @@ class Medal
|
|
|
6091
6269
|
const height = medalDisplaySize.y;
|
|
6092
6270
|
const x = overlayCanvas.width - width;
|
|
6093
6271
|
const y = -height*hidePercent;
|
|
6272
|
+
const backgroundColor = hsl(0,0,.9);
|
|
6094
6273
|
|
|
6095
6274
|
// draw containing rect and clip to that region
|
|
6096
6275
|
context.save();
|
|
6097
6276
|
context.beginPath();
|
|
6098
|
-
context.fillStyle =
|
|
6277
|
+
context.fillStyle = backgroundColor.toString();
|
|
6099
6278
|
context.strokeStyle = BLACK.toString();
|
|
6100
6279
|
context.lineWidth = 3;
|
|
6101
6280
|
context.rect(x, y, width, height);
|
|
@@ -6897,24 +7076,25 @@ function glPolyStrip(points)
|
|
|
6897
7076
|
return strip;
|
|
6898
7077
|
}
|
|
6899
7078
|
/**
|
|
6900
|
-
* LittleJS Newgrounds
|
|
7079
|
+
* LittleJS Newgrounds Plugin
|
|
6901
7080
|
* - NewgroundsMedal extends Medal with Newgrounds API functionality
|
|
6902
|
-
* - Call new NewgroundsPlugin() to setup Newgrounds
|
|
7081
|
+
* - Call new NewgroundsPlugin(app_id) to setup Newgrounds
|
|
6903
7082
|
* - Uses CryptoJS for encryption if optional cipher is provided
|
|
7083
|
+
* - provides functions to interact with medals scoreboards
|
|
6904
7084
|
* - Keeps connection alive and logs views
|
|
6905
|
-
*
|
|
6906
|
-
* - Functions to unlock medals
|
|
7085
|
+
* @namespace Newgrounds
|
|
6907
7086
|
*/
|
|
6908
7087
|
|
|
6909
7088
|
/** Global Newgrounds object
|
|
6910
7089
|
* @type {NewgroundsPlugin}
|
|
6911
|
-
* @memberof
|
|
7090
|
+
* @memberof Newgrounds */
|
|
6912
7091
|
let newgrounds;
|
|
6913
7092
|
|
|
6914
7093
|
///////////////////////////////////////////////////////////////////////////////
|
|
6915
7094
|
/**
|
|
6916
7095
|
* Newgrounds medal auto unlocks in newgrounds API
|
|
6917
7096
|
* @extends Medal
|
|
7097
|
+
* @memberof Newgrounds
|
|
6918
7098
|
*/
|
|
6919
7099
|
class NewgroundsMedal extends Medal
|
|
6920
7100
|
{
|
|
@@ -6939,6 +7119,7 @@ class NewgroundsMedal extends Medal
|
|
|
6939
7119
|
///////////////////////////////////////////////////////////////////////////////
|
|
6940
7120
|
/**
|
|
6941
7121
|
* Newgrounds API object
|
|
7122
|
+
* @memberof Newgrounds
|
|
6942
7123
|
*/
|
|
6943
7124
|
class NewgroundsPlugin
|
|
6944
7125
|
{
|
|
@@ -6972,7 +7153,7 @@ class NewgroundsPlugin
|
|
|
6972
7153
|
// get medals
|
|
6973
7154
|
const medalsResult = this.call('Medal.getList');
|
|
6974
7155
|
this.medals = medalsResult ? medalsResult.result.data['medals'] : [];
|
|
6975
|
-
debugMedals &&
|
|
7156
|
+
debugMedals && LOG(this.medals);
|
|
6976
7157
|
for (const newgroundsMedal of this.medals)
|
|
6977
7158
|
{
|
|
6978
7159
|
const medal = medals[newgroundsMedal['id']];
|
|
@@ -6995,7 +7176,7 @@ class NewgroundsPlugin
|
|
|
6995
7176
|
// get scoreboards
|
|
6996
7177
|
const scoreboardResult = this.call('ScoreBoard.getBoards');
|
|
6997
7178
|
this.scoreboards = scoreboardResult ? scoreboardResult.result.data.scoreboards : [];
|
|
6998
|
-
debugMedals &&
|
|
7179
|
+
debugMedals && LOG(this.scoreboards);
|
|
6999
7180
|
|
|
7000
7181
|
// keep the session alive with a ping every minute
|
|
7001
7182
|
const keepAliveMS = 60 * 1e3;
|
|
@@ -7064,29 +7245,32 @@ class NewgroundsPlugin
|
|
|
7064
7245
|
try { xmlHttp.send(formData); }
|
|
7065
7246
|
catch(e)
|
|
7066
7247
|
{
|
|
7067
|
-
debugMedals &&
|
|
7248
|
+
debugMedals && LOG('newgrounds call failed', e);
|
|
7068
7249
|
return;
|
|
7069
7250
|
}
|
|
7070
|
-
debugMedals &&
|
|
7251
|
+
debugMedals && LOG(xmlHttp.responseText);
|
|
7071
7252
|
return xmlHttp.responseText && JSON.parse(xmlHttp.responseText);
|
|
7072
7253
|
}
|
|
7073
7254
|
}
|
|
7074
7255
|
/**
|
|
7075
7256
|
* LittleJS Post Processing Plugin
|
|
7076
7257
|
* - Supports shadertoy style post processing shaders
|
|
7077
|
-
* - call new
|
|
7258
|
+
* - call new PostProcessPlugin() to setup post processing
|
|
7078
7259
|
* - can be enabled to pass other canvases through a final shader
|
|
7260
|
+
* @namespace PostProcess
|
|
7079
7261
|
*/
|
|
7080
7262
|
|
|
7081
7263
|
///////////////////////////////////////////////////////////////////////////////
|
|
7082
7264
|
|
|
7083
7265
|
/** Global Post Process plugin object
|
|
7084
|
-
* @type {PostProcessPlugin}
|
|
7266
|
+
* @type {PostProcessPlugin}
|
|
7267
|
+
* @memberof PostProcess */
|
|
7085
7268
|
let postProcess;
|
|
7086
7269
|
|
|
7087
7270
|
/////////////////////////////////////////////////////////////////////////
|
|
7088
7271
|
/**
|
|
7089
7272
|
* UI System Global Object
|
|
7273
|
+
* @memberof PostProcess
|
|
7090
7274
|
*/
|
|
7091
7275
|
class PostProcessPlugin
|
|
7092
7276
|
{
|
|
@@ -7194,12 +7378,15 @@ class PostProcessPlugin
|
|
|
7194
7378
|
}
|
|
7195
7379
|
/**
|
|
7196
7380
|
* LittleJS ZzFXM Plugin
|
|
7381
|
+
* @namespace ZzFXM
|
|
7197
7382
|
*/
|
|
7198
7383
|
|
|
7199
7384
|
/**
|
|
7200
7385
|
* Music Object - Stores a zzfx music track for later use
|
|
7201
7386
|
*
|
|
7202
7387
|
* <a href=https://keithclark.github.io/ZzFXM/>Create music with the ZzFXM tracker.</a>
|
|
7388
|
+
* @extends Sound
|
|
7389
|
+
* @memberof ZzFXM
|
|
7203
7390
|
* @example
|
|
7204
7391
|
* // create some music
|
|
7205
7392
|
* const music_example = new Music(
|
|
@@ -7258,7 +7445,8 @@ class ZzFXMusic extends Sound
|
|
|
7258
7445
|
* @param {Array} patterns - Array of pattern data
|
|
7259
7446
|
* @param {Array} sequence - Array of pattern indexes
|
|
7260
7447
|
* @param {number} [BPM] - Playback speed of the song in BPM
|
|
7261
|
-
* @return {Array} - Left and right channel sample data
|
|
7448
|
+
* @return {Array} - Left and right channel sample data
|
|
7449
|
+
* @memberof ZzFXM */
|
|
7262
7450
|
function zzfxM(instruments, patterns, sequence, BPM = 125)
|
|
7263
7451
|
{
|
|
7264
7452
|
let i, j, k;
|
|
@@ -7361,17 +7549,20 @@ function zzfxM(instruments, patterns, sequence, BPM = 125)
|
|
|
7361
7549
|
* - Buttons
|
|
7362
7550
|
* - Checkboxes
|
|
7363
7551
|
* - Images
|
|
7552
|
+
* @namespace UISystem
|
|
7364
7553
|
*/
|
|
7365
7554
|
|
|
7366
7555
|
///////////////////////////////////////////////////////////////////////////////
|
|
7367
7556
|
|
|
7368
7557
|
/** Global UI system plugin object
|
|
7369
|
-
* @type {UISystemPlugin}
|
|
7558
|
+
* @type {UISystemPlugin}
|
|
7559
|
+
* @memberof UISystem */
|
|
7370
7560
|
let uiSystem;
|
|
7371
7561
|
|
|
7372
7562
|
///////////////////////////////////////////////////////////////////////////////
|
|
7373
7563
|
/**
|
|
7374
7564
|
* UI System Global Object
|
|
7565
|
+
* @memberof UISystem
|
|
7375
7566
|
*/
|
|
7376
7567
|
class UISystemPlugin
|
|
7377
7568
|
{
|
|
@@ -7416,10 +7607,12 @@ class UISystemPlugin
|
|
|
7416
7607
|
this.uiObjects = [];
|
|
7417
7608
|
/** @property {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} - Context to render UI elements to */
|
|
7418
7609
|
this.uiContext = context;
|
|
7419
|
-
/** @property {UIObject} - Top most object user is over */
|
|
7420
|
-
this.hoverObject = undefined;
|
|
7421
7610
|
/** @property {UIObject} - Object user is currently interacting with */
|
|
7422
7611
|
this.activeObject = undefined;
|
|
7612
|
+
/** @property {UIObject} - Top most object user is over */
|
|
7613
|
+
this.hoverObject = undefined;
|
|
7614
|
+
/** @property {UIObject} - Hover object at start of update */
|
|
7615
|
+
this.lastHoverObject = undefined;
|
|
7423
7616
|
|
|
7424
7617
|
engineAddPlugin(uiUpdate, uiRender);
|
|
7425
7618
|
|
|
@@ -7450,6 +7643,7 @@ class UISystemPlugin
|
|
|
7450
7643
|
updateInvisibleObject(o);
|
|
7451
7644
|
}
|
|
7452
7645
|
// reset hover object at start of update
|
|
7646
|
+
uiSystem.lastHoverObject = uiSystem.hoverObject;
|
|
7453
7647
|
uiSystem.hoverObject = undefined;
|
|
7454
7648
|
for (let i = uiSystem.uiObjects.length; i--;)
|
|
7455
7649
|
{
|
|
@@ -7482,6 +7676,13 @@ class UISystemPlugin
|
|
|
7482
7676
|
* @param {number} [cornerRadius=uiSystem.defaultCornerRadius] */
|
|
7483
7677
|
drawRect(pos, size, color=uiSystem.defaultColor, lineWidth=uiSystem.defaultLineWidth, lineColor=uiSystem.defaultLineColor, cornerRadius=uiSystem.defaultCornerRadius)
|
|
7484
7678
|
{
|
|
7679
|
+
ASSERT(isVector2(pos), 'pos must be a vec2');
|
|
7680
|
+
ASSERT(isVector2(size), 'size must be a vec2');
|
|
7681
|
+
ASSERT(isColor(color), 'color must be a color');
|
|
7682
|
+
ASSERT(isNumber(lineWidth), 'lineWidth must be a number');
|
|
7683
|
+
ASSERT(isColor(lineColor), 'lineColor must be a color');
|
|
7684
|
+
ASSERT(isNumber(cornerRadius), 'cornerRadius must be a number');
|
|
7685
|
+
|
|
7485
7686
|
const context = uiSystem.uiContext;
|
|
7486
7687
|
context.fillStyle = color.toString();
|
|
7487
7688
|
context.beginPath();
|
|
@@ -7505,6 +7706,11 @@ class UISystemPlugin
|
|
|
7505
7706
|
* @param {Color} [lineColor=uiSystem.defaultLineColor] */
|
|
7506
7707
|
drawLine(posA, posB, lineWidth=uiSystem.defaultLineWidth, lineColor=uiSystem.defaultLineColor)
|
|
7507
7708
|
{
|
|
7709
|
+
ASSERT(isVector2(posA), 'posA must be a vec2');
|
|
7710
|
+
ASSERT(isVector2(posB), 'posB must be a vec2');
|
|
7711
|
+
ASSERT(isNumber(lineWidth), 'lineWidth must be a number');
|
|
7712
|
+
ASSERT(isColor(lineColor), 'lineColor must be a color');
|
|
7713
|
+
|
|
7508
7714
|
const context = uiSystem.uiContext;
|
|
7509
7715
|
context.strokeStyle = lineColor.toString();
|
|
7510
7716
|
context.lineWidth = lineWidth;
|
|
@@ -7540,12 +7746,37 @@ class UISystemPlugin
|
|
|
7540
7746
|
{
|
|
7541
7747
|
drawTextScreen(text, pos, size.y, color, lineWidth, lineColor, align, font, applyMaxWidth ? size.x : undefined, uiSystem.uiContext);
|
|
7542
7748
|
}
|
|
7749
|
+
|
|
7750
|
+
/**
|
|
7751
|
+
* @callback DragAndDropCallback - Callback for drag and drop events
|
|
7752
|
+
* @param {DragEvent} event - The drag event
|
|
7753
|
+
* @memberof UISystem
|
|
7754
|
+
*/
|
|
7755
|
+
|
|
7756
|
+
/** Setup drag and drop event handlers
|
|
7757
|
+
* Automatically prevents defaults and calls the given functions
|
|
7758
|
+
* @param {DragAndDropCallback} [onDrop] - when a file is dropped
|
|
7759
|
+
* @param {DragAndDropCallback} [onDragEnter] - when a file is dragged onto the window
|
|
7760
|
+
* @param {DragAndDropCallback} [onDragLeave] - when a file is dragged off the window
|
|
7761
|
+
* @param {DragAndDropCallback} [onDragOver] - continously when dragging over */
|
|
7762
|
+
setupDragAndDrop(onDrop, onDragEnter, onDragLeave, onDragOver)
|
|
7763
|
+
{
|
|
7764
|
+
function setCallback(callback, listenerType)
|
|
7765
|
+
{
|
|
7766
|
+
function listener(e) { e.preventDefault(); callback && callback(e); }
|
|
7767
|
+
document.addEventListener(listenerType, listener);
|
|
7768
|
+
}
|
|
7769
|
+
setCallback(onDrop, 'drop');
|
|
7770
|
+
setCallback(onDragEnter, 'dragenter');
|
|
7771
|
+
setCallback(onDragLeave, 'dragleave');
|
|
7772
|
+
setCallback(onDragOver, 'dragover');
|
|
7773
|
+
}
|
|
7543
7774
|
}
|
|
7544
7775
|
|
|
7545
7776
|
///////////////////////////////////////////////////////////////////////////////
|
|
7546
7777
|
/**
|
|
7547
7778
|
* UI Object - Base level object for all UI elements
|
|
7548
|
-
*/
|
|
7779
|
+
* @memberof UISystem */
|
|
7549
7780
|
class UIObject
|
|
7550
7781
|
{
|
|
7551
7782
|
/** Create a UIObject
|
|
@@ -7554,6 +7785,9 @@ class UIObject
|
|
|
7554
7785
|
*/
|
|
7555
7786
|
constructor(pos=vec2(), size=vec2())
|
|
7556
7787
|
{
|
|
7788
|
+
ASSERT(isVector2(pos), 'ui object pos must be a vec2');
|
|
7789
|
+
ASSERT(isVector2(size), 'ui object size must be a vec2');
|
|
7790
|
+
|
|
7557
7791
|
/** @property {Vector2} - Local position of the object */
|
|
7558
7792
|
this.localPos = pos.copy();
|
|
7559
7793
|
/** @property {Vector2} - Screen space position of the object */
|
|
@@ -7561,21 +7795,21 @@ class UIObject
|
|
|
7561
7795
|
/** @property {Vector2} - Screen space size of the object */
|
|
7562
7796
|
this.size = size.copy();
|
|
7563
7797
|
/** @property {Color} - Color of the object */
|
|
7564
|
-
this.color = uiSystem.defaultColor;
|
|
7798
|
+
this.color = uiSystem.defaultColor.copy();
|
|
7565
7799
|
/** @property {Color} - Color of the object when active, uses color if undefined */
|
|
7566
7800
|
this.activeColor = undefined;
|
|
7567
7801
|
/** @property {string} - Text for this ui object */
|
|
7568
7802
|
this.text = undefined;
|
|
7569
7803
|
/** @property {Color} - Color when disabled */
|
|
7570
|
-
this.disabledColor = uiSystem.defaultDisabledColor;
|
|
7804
|
+
this.disabledColor = uiSystem.defaultDisabledColor.copy();
|
|
7571
7805
|
/** @property {boolean} - Is this object disabled? */
|
|
7572
7806
|
this.disabled = false;
|
|
7573
7807
|
/** @property {Color} - Color for text */
|
|
7574
|
-
this.textColor = uiSystem.defaultTextColor
|
|
7808
|
+
this.textColor = uiSystem.defaultTextColor.copy()
|
|
7575
7809
|
/** @property {Color} - Color used when hovering over the object */
|
|
7576
|
-
this.hoverColor = uiSystem.defaultHoverColor
|
|
7810
|
+
this.hoverColor = uiSystem.defaultHoverColor.copy()
|
|
7577
7811
|
/** @property {Color} - Color for line drawing */
|
|
7578
|
-
this.lineColor = uiSystem.defaultLineColor
|
|
7812
|
+
this.lineColor = uiSystem.defaultLineColor.copy()
|
|
7579
7813
|
/** @property {number} - Width for line drawing */
|
|
7580
7814
|
this.lineWidth = uiSystem.defaultLineWidth;
|
|
7581
7815
|
/** @property {number} - Corner radius for rounded rects */
|
|
@@ -7632,7 +7866,7 @@ class UIObject
|
|
|
7632
7866
|
/** Update the object, called automatically by plugin once each frame */
|
|
7633
7867
|
update()
|
|
7634
7868
|
{
|
|
7635
|
-
const wasHover = this
|
|
7869
|
+
const wasHover = uiSystem.lastHoverObject === this;
|
|
7636
7870
|
const isActive = this.isActiveObject();
|
|
7637
7871
|
const mouseDown = mouseIsDown(0);
|
|
7638
7872
|
const mousePress = this.dragActivate ? mouseDown : mouseWasPressed(0);
|
|
@@ -7645,15 +7879,14 @@ class UIObject
|
|
|
7645
7879
|
}
|
|
7646
7880
|
if (this.isHoverObject())
|
|
7647
7881
|
{
|
|
7648
|
-
if (mousePress)
|
|
7649
|
-
inputClearKey(0,0,0,1,0); // clear mouse was pressed state
|
|
7650
7882
|
if (!this.disabled)
|
|
7651
7883
|
{
|
|
7652
7884
|
if (mousePress)
|
|
7653
7885
|
{
|
|
7654
7886
|
if (this.interactive)
|
|
7655
7887
|
{
|
|
7656
|
-
this.
|
|
7888
|
+
if (!this.dragActivate || (!wasHover || mouseWasPressed(0)))
|
|
7889
|
+
this.onPress();
|
|
7657
7890
|
if (this.soundPress)
|
|
7658
7891
|
this.soundPress.play();
|
|
7659
7892
|
if (uiSystem.activeObject && !isActive)
|
|
@@ -7661,13 +7894,15 @@ class UIObject
|
|
|
7661
7894
|
uiSystem.activeObject = this;
|
|
7662
7895
|
}
|
|
7663
7896
|
}
|
|
7664
|
-
if (!mouseDown &&
|
|
7897
|
+
if (!mouseDown && this.isActiveObject() && this.interactive)
|
|
7665
7898
|
{
|
|
7666
7899
|
this.onClick();
|
|
7667
7900
|
if (this.soundClick)
|
|
7668
7901
|
this.soundClick.play();
|
|
7669
7902
|
}
|
|
7670
7903
|
}
|
|
7904
|
+
// clear mouse was pressed state even when disabled
|
|
7905
|
+
mousePress && inputClearKey(0,0,0,1,0);
|
|
7671
7906
|
}
|
|
7672
7907
|
if (isActive)
|
|
7673
7908
|
if (!mouseDown || (this.dragActivate && !this.isHoverObject()))
|
|
@@ -7678,6 +7913,7 @@ class UIObject
|
|
|
7678
7913
|
uiSystem.activeObject = undefined;
|
|
7679
7914
|
}
|
|
7680
7915
|
|
|
7916
|
+
// call enter/leave events
|
|
7681
7917
|
if (this.isHoverObject() !== wasHover)
|
|
7682
7918
|
this.isHoverObject() ? this.onEnter() : this.onLeave();
|
|
7683
7919
|
}
|
|
@@ -7688,7 +7924,7 @@ class UIObject
|
|
|
7688
7924
|
if (!this.size.x || !this.size.y) return;
|
|
7689
7925
|
|
|
7690
7926
|
const lineColor = this.interactive && this.isActiveObject() && !this.disabled ? this.color : this.lineColor;
|
|
7691
|
-
const color = this.
|
|
7927
|
+
const color = this.disabled ? this.disabledColor : this.interactive ? this.isActiveObject() ? this.activeColor || this.color : this.isHoverObject() ? this.hoverColor : this.color : this.color;
|
|
7692
7928
|
uiSystem.drawRect(this.pos, this.size, color, this.lineWidth, lineColor, this.cornerRadius);
|
|
7693
7929
|
}
|
|
7694
7930
|
|
|
@@ -7739,6 +7975,7 @@ class UIObject
|
|
|
7739
7975
|
/**
|
|
7740
7976
|
* UIText - A UI object that displays text
|
|
7741
7977
|
* @extends UIObject
|
|
7978
|
+
* @memberof UISystem
|
|
7742
7979
|
*/
|
|
7743
7980
|
class UIText extends UIObject
|
|
7744
7981
|
{
|
|
@@ -7753,6 +7990,10 @@ class UIText extends UIObject
|
|
|
7753
7990
|
{
|
|
7754
7991
|
super(pos, size);
|
|
7755
7992
|
|
|
7993
|
+
ASSERT(isString(text), 'ui text must be a string');
|
|
7994
|
+
ASSERT(['left','center','right'].includes(align), 'ui text align must be left, center, or right');
|
|
7995
|
+
ASSERT(isString(font), 'ui text font must be a string');
|
|
7996
|
+
|
|
7756
7997
|
// set properties
|
|
7757
7998
|
this.text = text;
|
|
7758
7999
|
this.align = align;
|
|
@@ -7772,6 +8013,7 @@ class UIText extends UIObject
|
|
|
7772
8013
|
/**
|
|
7773
8014
|
* UITile - A UI object that displays a tile image
|
|
7774
8015
|
* @extends UIObject
|
|
8016
|
+
* @memberof UISystem
|
|
7775
8017
|
*/
|
|
7776
8018
|
class UITile extends UIObject
|
|
7777
8019
|
{
|
|
@@ -7786,15 +8028,19 @@ class UITile extends UIObject
|
|
|
7786
8028
|
constructor(pos, size, tileInfo, color=WHITE, angle=0, mirror=false)
|
|
7787
8029
|
{
|
|
7788
8030
|
super(pos, size);
|
|
8031
|
+
|
|
8032
|
+
ASSERT(tileInfo instanceof TileInfo, 'ui tile tileInfo must be a TileInfo');
|
|
8033
|
+
ASSERT(isColor(color), 'ui tile color must be a color');
|
|
8034
|
+
ASSERT(isNumber(angle), 'ui tile angle must be a number');
|
|
8035
|
+
|
|
7789
8036
|
/** @property {TileInfo} - Tile image to use */
|
|
7790
8037
|
this.tileInfo = tileInfo;
|
|
7791
8038
|
/** @property {number} - Angle to rotate in radians */
|
|
7792
8039
|
this.angle = angle;
|
|
7793
8040
|
/** @property {boolean} - Should it be mirrored? */
|
|
7794
8041
|
this.mirror = mirror;
|
|
7795
|
-
|
|
7796
8042
|
// set properties
|
|
7797
|
-
this.color = color;
|
|
8043
|
+
this.color = color.copy();
|
|
7798
8044
|
}
|
|
7799
8045
|
render()
|
|
7800
8046
|
{
|
|
@@ -7806,6 +8052,7 @@ class UITile extends UIObject
|
|
|
7806
8052
|
/**
|
|
7807
8053
|
* UIButton - A UI object that acts as a button
|
|
7808
8054
|
* @extends UIObject
|
|
8055
|
+
* @memberof UISystem
|
|
7809
8056
|
*/
|
|
7810
8057
|
class UIButton extends UIObject
|
|
7811
8058
|
{
|
|
@@ -7819,9 +8066,12 @@ class UIButton extends UIObject
|
|
|
7819
8066
|
{
|
|
7820
8067
|
super(pos, size);
|
|
7821
8068
|
|
|
8069
|
+
ASSERT(isString(text), 'ui button must be a string');
|
|
8070
|
+
ASSERT(isColor(color), 'ui button color must be a color');
|
|
8071
|
+
|
|
7822
8072
|
// set properties
|
|
7823
8073
|
this.text = text;
|
|
7824
|
-
this.color = color
|
|
8074
|
+
this.color = color.copy()
|
|
7825
8075
|
this.interactive = true;
|
|
7826
8076
|
}
|
|
7827
8077
|
render()
|
|
@@ -7839,6 +8089,7 @@ class UIButton extends UIObject
|
|
|
7839
8089
|
/**
|
|
7840
8090
|
* UICheckbox - A UI object that acts as a checkbox
|
|
7841
8091
|
* @extends UIObject
|
|
8092
|
+
* @memberof UISystem
|
|
7842
8093
|
*/
|
|
7843
8094
|
class UICheckbox extends UIObject
|
|
7844
8095
|
{
|
|
@@ -7852,12 +8103,15 @@ class UICheckbox extends UIObject
|
|
|
7852
8103
|
constructor(pos, size, checked=false, text='', color=uiSystem.defaultButtonColor)
|
|
7853
8104
|
{
|
|
7854
8105
|
super(pos, size);
|
|
8106
|
+
|
|
8107
|
+
ASSERT(isString(text), 'ui checkbox must be a string');
|
|
8108
|
+
ASSERT(isColor(color), 'ui checkbox color must be a color');
|
|
8109
|
+
|
|
7855
8110
|
/** @property {boolean} - Current percentage value of this scrollbar 0-1 */
|
|
7856
8111
|
this.checked = checked;
|
|
7857
|
-
|
|
7858
8112
|
// set properties
|
|
7859
8113
|
this.text = text;
|
|
7860
|
-
this.color = color;
|
|
8114
|
+
this.color = color.copy();
|
|
7861
8115
|
this.interactive = true;
|
|
7862
8116
|
}
|
|
7863
8117
|
onClick()
|
|
@@ -7889,6 +8143,7 @@ class UICheckbox extends UIObject
|
|
|
7889
8143
|
/**
|
|
7890
8144
|
* UIScrollbar - A UI object that acts as a scrollbar
|
|
7891
8145
|
* @extends UIObject
|
|
8146
|
+
* @memberof UISystem
|
|
7892
8147
|
*/
|
|
7893
8148
|
class UIScrollbar extends UIObject
|
|
7894
8149
|
{
|
|
@@ -7904,14 +8159,19 @@ class UIScrollbar extends UIObject
|
|
|
7904
8159
|
{
|
|
7905
8160
|
super(pos, size);
|
|
7906
8161
|
|
|
8162
|
+
ASSERT(isNumber(value), 'ui scrollbar value must be a number');
|
|
8163
|
+
ASSERT(isString(text), 'ui scrollbar must be a string');
|
|
8164
|
+
ASSERT(isColor(color), 'ui scrollbar color must be a color');
|
|
8165
|
+
ASSERT(isColor(handleColor), 'ui scrollbar handleColor must be a color');
|
|
8166
|
+
|
|
7907
8167
|
/** @property {number} - Current percentage value of this scrollbar 0-1 */
|
|
7908
8168
|
this.value = value;
|
|
7909
8169
|
/** @property {Color} - Color for the handle part of the scrollbar */
|
|
7910
|
-
this.handleColor = handleColor;
|
|
8170
|
+
this.handleColor = handleColor.copy();
|
|
7911
8171
|
|
|
7912
8172
|
// set properties
|
|
7913
8173
|
this.text = text;
|
|
7914
|
-
this.color = color;
|
|
8174
|
+
this.color = color.copy();
|
|
7915
8175
|
this.interactive = true;
|
|
7916
8176
|
}
|
|
7917
8177
|
update()
|
|
@@ -7919,29 +8179,43 @@ class UIScrollbar extends UIObject
|
|
|
7919
8179
|
super.update();
|
|
7920
8180
|
if (this.isActiveObject() && this.interactive)
|
|
7921
8181
|
{
|
|
8182
|
+
// handle horizontal or vertical scrollbar
|
|
8183
|
+
const isHorizontal = this.size.x > this.size.y;
|
|
8184
|
+
const handleSize = isHorizontal ? this.size.y : this.size.x;
|
|
8185
|
+
const barSize = isHorizontal ? this.size.x : this.size.y;
|
|
8186
|
+
const centerPos = isHorizontal ? this.pos.x : this.pos.y;
|
|
8187
|
+
|
|
7922
8188
|
// check if value changed
|
|
7923
|
-
const
|
|
7924
|
-
const
|
|
7925
|
-
const
|
|
7926
|
-
const p2 = this.pos.x + handleWidth/2;
|
|
8189
|
+
const handleWidth = barSize - handleSize;
|
|
8190
|
+
const p1 = centerPos - handleWidth/2;
|
|
8191
|
+
const p2 = centerPos + handleWidth/2;
|
|
7927
8192
|
const oldValue = this.value;
|
|
7928
|
-
this.value =
|
|
8193
|
+
this.value = isHorizontal ?
|
|
8194
|
+
percent(mousePosScreen.x, p1, p2) :
|
|
8195
|
+
percent(mousePosScreen.y, p2, p1);
|
|
7929
8196
|
this.value === oldValue || this.onChange();
|
|
7930
8197
|
}
|
|
7931
8198
|
}
|
|
7932
8199
|
render()
|
|
7933
8200
|
{
|
|
7934
8201
|
super.render();
|
|
7935
|
-
|
|
8202
|
+
|
|
8203
|
+
// handle horizontal or vertical scrollbar
|
|
8204
|
+
const isHorizontal = this.size.x > this.size.y;
|
|
8205
|
+
const handleSize = isHorizontal ? this.size.y : this.size.x;
|
|
8206
|
+
const barSize = isHorizontal ? this.size.x : this.size.y;
|
|
8207
|
+
const centerPos = isHorizontal ? this.pos.x : this.pos.y;
|
|
8208
|
+
|
|
7936
8209
|
// draw the scrollbar handle
|
|
7937
|
-
const
|
|
7938
|
-
const
|
|
7939
|
-
const
|
|
7940
|
-
const
|
|
7941
|
-
|
|
8210
|
+
const handleWidth = barSize - handleSize;
|
|
8211
|
+
const p1 = centerPos - handleWidth/2;
|
|
8212
|
+
const p2 = centerPos + handleWidth/2;
|
|
8213
|
+
const handlePos = isHorizontal ?
|
|
8214
|
+
vec2(lerp(p1, p2, this.value), this.pos.y) :
|
|
8215
|
+
vec2(this.pos.x, lerp(p2, p1, this.value))
|
|
7942
8216
|
const handleColor = this.disabled ? this.disabledColor :
|
|
7943
8217
|
this.interactive && this.isActiveObject() ? this.color : this.handleColor;
|
|
7944
|
-
uiSystem.drawRect(handlePos, handleSize, handleColor, this.lineWidth, this.lineColor, this.cornerRadius);
|
|
8218
|
+
uiSystem.drawRect(handlePos, vec2(handleSize), handleColor, this.lineWidth, this.lineColor, this.cornerRadius);
|
|
7945
8219
|
|
|
7946
8220
|
// draw the text scaled to fit on the scrollbar
|
|
7947
8221
|
const textSize = this.getTextSize();
|
|
@@ -7988,6 +8262,7 @@ function box2dSetDebug(enable) { box2dDebug = enable; }
|
|
|
7988
8262
|
* - Each object has a Box2D body which can have multiple fixtures and joints
|
|
7989
8263
|
* - Provides interface for Box2D body and fixture functions
|
|
7990
8264
|
* @extends EngineObject
|
|
8265
|
+
* @memberof Box2D
|
|
7991
8266
|
*/
|
|
7992
8267
|
class Box2dObject extends EngineObject
|
|
7993
8268
|
{
|
|
@@ -7999,7 +8274,7 @@ class Box2dObject extends EngineObject
|
|
|
7999
8274
|
* @param {Color} [color]
|
|
8000
8275
|
* @param {number} [bodyType]
|
|
8001
8276
|
* @param {number} [renderOrder] */
|
|
8002
|
-
constructor(pos
|
|
8277
|
+
constructor(pos, size, tileInfo, angle=0, color, bodyType=box2d.bodyTypeDynamic, renderOrder=0)
|
|
8003
8278
|
{
|
|
8004
8279
|
super(pos, size, tileInfo, angle, color, renderOrder);
|
|
8005
8280
|
|
|
@@ -8013,7 +8288,7 @@ class Box2dObject extends EngineObject
|
|
|
8013
8288
|
this.lineColor = BLACK;
|
|
8014
8289
|
}
|
|
8015
8290
|
|
|
8016
|
-
/** Destroy this object and
|
|
8291
|
+
/** Destroy this object and its physics body */
|
|
8017
8292
|
destroy()
|
|
8018
8293
|
{
|
|
8019
8294
|
// destroy physics body, fixtures, and joints
|
|
@@ -8497,6 +8772,7 @@ class Box2dRaycastResult
|
|
|
8497
8772
|
* Box2D Joint
|
|
8498
8773
|
* - Base class for Box2D joints
|
|
8499
8774
|
* - A joint is used to connect objects together
|
|
8775
|
+
* @memberof Box2D
|
|
8500
8776
|
*/
|
|
8501
8777
|
class Box2dJoint
|
|
8502
8778
|
{
|
|
@@ -8552,6 +8828,7 @@ class Box2dJoint
|
|
|
8552
8828
|
* - This a soft constraint with a max force
|
|
8553
8829
|
* - This allows the constraint to stretch and without applying huge forces
|
|
8554
8830
|
* @extends Box2dJoint
|
|
8831
|
+
* @memberof Box2D
|
|
8555
8832
|
*/
|
|
8556
8833
|
class Box2dTargetJoint extends Box2dJoint
|
|
8557
8834
|
{
|
|
@@ -8601,6 +8878,7 @@ class Box2dTargetJoint extends Box2dJoint
|
|
|
8601
8878
|
* - Constrains two points on two objects to remain at a fixed distance
|
|
8602
8879
|
* - You can view this as a massless, rigid rod
|
|
8603
8880
|
* @extends Box2dJoint
|
|
8881
|
+
* @memberof Box2D
|
|
8604
8882
|
*/
|
|
8605
8883
|
class Box2dDistanceJoint extends Box2dJoint
|
|
8606
8884
|
{
|
|
@@ -8664,6 +8942,7 @@ class Box2dDistanceJoint extends Box2dJoint
|
|
|
8664
8942
|
* Box2D Pin Joint
|
|
8665
8943
|
* - Pins two objects together at a point
|
|
8666
8944
|
* @extends Box2dDistanceJoint
|
|
8945
|
+
* @memberof Box2D
|
|
8667
8946
|
*/
|
|
8668
8947
|
class Box2dPinJoint extends Box2dDistanceJoint
|
|
8669
8948
|
{
|
|
@@ -8683,6 +8962,7 @@ class Box2dPinJoint extends Box2dDistanceJoint
|
|
|
8683
8962
|
* Box2D Rope Joint
|
|
8684
8963
|
* - Enforces a maximum distance between two points on two objects
|
|
8685
8964
|
* @extends Box2dJoint
|
|
8965
|
+
* @memberof Box2D
|
|
8686
8966
|
*/
|
|
8687
8967
|
class Box2dRopeJoint extends Box2dJoint
|
|
8688
8968
|
{
|
|
@@ -8735,6 +9015,7 @@ class Box2dRopeJoint extends Box2dJoint
|
|
|
8735
9015
|
* - You can use a motor to drive the relative rotation about the shared point
|
|
8736
9016
|
* - A maximum motor torque is provided so that infinite forces are not generated
|
|
8737
9017
|
* @extends Box2dJoint
|
|
9018
|
+
* @memberof Box2D
|
|
8738
9019
|
*/
|
|
8739
9020
|
class Box2dRevoluteJoint extends Box2dJoint
|
|
8740
9021
|
{
|
|
@@ -8836,6 +9117,7 @@ class Box2dRevoluteJoint extends Box2dJoint
|
|
|
8836
9117
|
* - Either joint can be a revolute or prismatic joint
|
|
8837
9118
|
* - You specify a gear ratio to bind the motions together
|
|
8838
9119
|
* @extends Box2dJoint
|
|
9120
|
+
* @memberof Box2D
|
|
8839
9121
|
*/
|
|
8840
9122
|
class Box2dGearJoint extends Box2dJoint
|
|
8841
9123
|
{
|
|
@@ -8884,6 +9166,7 @@ class Box2dGearJoint extends Box2dJoint
|
|
|
8884
9166
|
* - You can use a joint limit to restrict the range of motion
|
|
8885
9167
|
* - You can use a joint motor to drive the motion or to model joint friction
|
|
8886
9168
|
* @extends Box2dJoint
|
|
9169
|
+
* @memberof Box2D
|
|
8887
9170
|
*/
|
|
8888
9171
|
class Box2dPrismaticJoint extends Box2dJoint
|
|
8889
9172
|
{
|
|
@@ -8993,6 +9276,7 @@ class Box2dPrismaticJoint extends Box2dJoint
|
|
|
8993
9276
|
* - You can use a joint motor to drive the motion or to model joint friction
|
|
8994
9277
|
* - This joint is designed for vehicle suspensions
|
|
8995
9278
|
* @extends Box2dJoint
|
|
9279
|
+
* @memberof Box2D
|
|
8996
9280
|
*/
|
|
8997
9281
|
class Box2dWheelJoint extends Box2dJoint
|
|
8998
9282
|
{
|
|
@@ -9088,6 +9372,7 @@ class Box2dWheelJoint extends Box2dJoint
|
|
|
9088
9372
|
* Box2D Weld Joint
|
|
9089
9373
|
* - Glues two objects together
|
|
9090
9374
|
* @extends Box2dJoint
|
|
9375
|
+
* @memberof Box2D
|
|
9091
9376
|
*/
|
|
9092
9377
|
class Box2dWeldJoint extends Box2dJoint
|
|
9093
9378
|
{
|
|
@@ -9146,6 +9431,7 @@ class Box2dWeldJoint extends Box2dJoint
|
|
|
9146
9431
|
* - Used to apply top-down friction
|
|
9147
9432
|
* - Provides 2D translational friction and angular friction
|
|
9148
9433
|
* @extends Box2dJoint
|
|
9434
|
+
* @memberof Box2D
|
|
9149
9435
|
*/
|
|
9150
9436
|
class Box2dFrictionJoint extends Box2dJoint
|
|
9151
9437
|
{
|
|
@@ -9200,6 +9486,7 @@ class Box2dFrictionJoint extends Box2dJoint
|
|
|
9200
9486
|
* - The pulley supports a ratio such that: length1 + ratio * length2 <= constant
|
|
9201
9487
|
* - The force transmitted is scaled by the ratio
|
|
9202
9488
|
* @extends Box2dJoint
|
|
9489
|
+
* @memberof Box2D
|
|
9203
9490
|
*/
|
|
9204
9491
|
class Box2dPulleyJoint extends Box2dJoint
|
|
9205
9492
|
{
|
|
@@ -9267,6 +9554,7 @@ class Box2dPulleyJoint extends Box2dJoint
|
|
|
9267
9554
|
* - Controls the relative motion between two objects
|
|
9268
9555
|
* - Typical usage is to control the movement of a object with respect to the ground
|
|
9269
9556
|
* @extends Box2dJoint
|
|
9557
|
+
* @memberof Box2D
|
|
9270
9558
|
*/
|
|
9271
9559
|
class Box2dMotorJoint extends Box2dJoint
|
|
9272
9560
|
{
|
|
@@ -9330,6 +9618,7 @@ class Box2dMotorJoint extends Box2dJoint
|
|
|
9330
9618
|
/**
|
|
9331
9619
|
* Box2D Global Object
|
|
9332
9620
|
* - Wraps Box2d world and provides global functions
|
|
9621
|
+
* @memberof Box2D
|
|
9333
9622
|
*/
|
|
9334
9623
|
class Box2dPlugin
|
|
9335
9624
|
{
|
|
@@ -9664,7 +9953,7 @@ async function box2dInit()
|
|
|
9664
9953
|
}
|
|
9665
9954
|
function box2dRender()
|
|
9666
9955
|
{
|
|
9667
|
-
if (box2dDebug || debugPhysics
|
|
9956
|
+
if (box2dDebug || debugPhysics)
|
|
9668
9957
|
box2d.world.DrawDebugData();
|
|
9669
9958
|
}
|
|
9670
9959
|
|