littlejsengine 1.9.7 → 1.9.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +22 -0
- package/README.md +13 -8
- package/dist/littlejs.d.ts +64 -69
- package/dist/littlejs.esm.js +284 -347
- package/dist/littlejs.esm.min.js +1 -1
- package/dist/littlejs.js +280 -343
- package/dist/littlejs.min.js +1 -1
- package/dist/littlejs.release.js +217 -312
- package/examples/box2d/game.js +138 -0
- package/examples/box2d/index.html +25 -0
- package/examples/box2d/scenes.js +412 -0
- package/examples/box2d/tiles.png +0 -0
- package/examples/breakout/game.js +3 -3
- package/examples/breakout/index.html +1 -0
- package/examples/platformer/gameEffects.js +2 -2
- package/examples/platformer/gameLevel.js +0 -1
- package/package.json +1 -1
- package/plugins/Box2D_v2.3.1_min.wasm.js +630 -0
- package/plugins/Box2D_v2.3.1_min.wasm.wasm +0 -0
- package/plugins/box2d.js +873 -0
- package/plugins/newgrounds.js +169 -0
- package/plugins/postProcess.js +101 -0
- package/src/engine.js +44 -26
- package/src/engineAudio.js +20 -12
- package/src/engineDebug.js +68 -33
- package/src/engineDraw.js +1 -1
- package/src/engineExport.js +4 -4
- package/src/engineMedals.js +39 -171
- package/src/engineObject.js +30 -2
- package/src/engineRelease.js +5 -2
- package/src/engineSettings.js +2 -2
- package/src/engineUtilities.js +75 -2
- package/src/engineWebGL.js +1 -94
package/dist/littlejs.esm.js
CHANGED
|
@@ -80,6 +80,20 @@ function debugRect(pos, size=vec2(), color='#fff', time=0, angle=0, fill=false)
|
|
|
80
80
|
debugPrimitives.push({pos, size:vec2(size), color, time:new Timer(time), angle, fill});
|
|
81
81
|
}
|
|
82
82
|
|
|
83
|
+
/** Draw a debug poly in world space
|
|
84
|
+
* @param {Vector2} pos
|
|
85
|
+
* @param {Array} points
|
|
86
|
+
* @param {String} [color]
|
|
87
|
+
* @param {Number} [time]
|
|
88
|
+
* @param {Number} [angle]
|
|
89
|
+
* @param {Boolean} [fill]
|
|
90
|
+
* @memberof Debug */
|
|
91
|
+
function debugPoly(pos, points, color='#fff', time=0, angle=0, fill=false)
|
|
92
|
+
{
|
|
93
|
+
ASSERT(typeof color == 'string', 'pass in css color strings');
|
|
94
|
+
debugPrimitives.push({pos, points, color, time:new Timer(time), angle, fill});
|
|
95
|
+
}
|
|
96
|
+
|
|
83
97
|
/** Draw a debug circle in world space
|
|
84
98
|
* @param {Vector2} pos
|
|
85
99
|
* @param {Number} [radius]
|
|
@@ -89,7 +103,7 @@ function debugRect(pos, size=vec2(), color='#fff', time=0, angle=0, fill=false)
|
|
|
89
103
|
* @memberof Debug */
|
|
90
104
|
function debugCircle(pos, radius=0, color='#fff', time=0, fill=false)
|
|
91
105
|
{
|
|
92
|
-
ASSERT(typeof color == 'string', 'pass in css color strings');
|
|
106
|
+
ASSERT(typeof color == 'string', 'pass in css color strings');
|
|
93
107
|
debugPrimitives.push({pos, size:radius, color, time:new Timer(time), angle:0, fill});
|
|
94
108
|
}
|
|
95
109
|
|
|
@@ -99,7 +113,11 @@ function debugCircle(pos, radius=0, color='#fff', time=0, fill=false)
|
|
|
99
113
|
* @param {Number} [time]
|
|
100
114
|
* @param {Number} [angle]
|
|
101
115
|
* @memberof Debug */
|
|
102
|
-
function debugPoint(pos, color, time, angle)
|
|
116
|
+
function debugPoint(pos, color, time, angle)
|
|
117
|
+
{
|
|
118
|
+
ASSERT(typeof color == 'string', 'pass in css color strings');
|
|
119
|
+
debugRect(pos, undefined, color, time, angle);
|
|
120
|
+
}
|
|
103
121
|
|
|
104
122
|
/** Draw a debug line in world space
|
|
105
123
|
* @param {Vector2} posA
|
|
@@ -110,19 +128,20 @@ function debugPoint(pos, color, time, angle) {debugRect(pos, undefined, color, t
|
|
|
110
128
|
* @memberof Debug */
|
|
111
129
|
function debugLine(posA, posB, color, thickness=.1, time)
|
|
112
130
|
{
|
|
131
|
+
ASSERT(typeof color == 'string', 'pass in css color strings');
|
|
113
132
|
const halfDelta = vec2((posB.x - posA.x)/2, (posB.y - posA.y)/2);
|
|
114
133
|
const size = vec2(thickness, halfDelta.length()*2);
|
|
115
134
|
debugRect(posA.add(halfDelta), size, color, time, halfDelta.angle(), true);
|
|
116
135
|
}
|
|
117
136
|
|
|
118
|
-
/** Draw a debug axis aligned bounding box in world space
|
|
137
|
+
/** Draw a debug combined axis aligned bounding box in world space
|
|
119
138
|
* @param {Vector2} pA - position A
|
|
120
139
|
* @param {Vector2} sA - size A
|
|
121
140
|
* @param {Vector2} pB - position B
|
|
122
141
|
* @param {Vector2} sB - size B
|
|
123
142
|
* @param {String} [color]
|
|
124
143
|
* @memberof Debug */
|
|
125
|
-
function
|
|
144
|
+
function debugOverlap(pA, sA, pB, sB, color)
|
|
126
145
|
{
|
|
127
146
|
const minPos = vec2(min(pA.x - sA.x/2, pB.x - sB.x/2), min(pA.y - sA.y/2, pB.y - sB.y/2));
|
|
128
147
|
const maxPos = vec2(max(pA.x + sA.x/2, pB.x + sB.x/2), max(pA.y + sA.y/2, pB.y + sB.y/2));
|
|
@@ -140,7 +159,7 @@ function debugAABB(pA, sA, pB, sB, color)
|
|
|
140
159
|
* @memberof Debug */
|
|
141
160
|
function debugText(text, pos, size=1, color='#fff', time=0, angle=0, font='monospace')
|
|
142
161
|
{
|
|
143
|
-
ASSERT(typeof color == 'string', 'pass in css color strings');
|
|
162
|
+
ASSERT(typeof color == 'string', 'pass in css color strings');
|
|
144
163
|
debugPrimitives.push({text, pos, size, color, time:new Timer(time), angle, font});
|
|
145
164
|
}
|
|
146
165
|
|
|
@@ -239,7 +258,7 @@ function debugRender()
|
|
|
239
258
|
const stickScale = 1;
|
|
240
259
|
const buttonScale = .2;
|
|
241
260
|
const centerPos = cameraPos;
|
|
242
|
-
const sticks =
|
|
261
|
+
const sticks = gamepadStickData[i];
|
|
243
262
|
for (let j = sticks.length; j--;)
|
|
244
263
|
{
|
|
245
264
|
const drawPos = centerPos.add(vec2(j*stickScale*2, i*stickScale*3));
|
|
@@ -259,6 +278,7 @@ function debugRender()
|
|
|
259
278
|
}
|
|
260
279
|
}
|
|
261
280
|
|
|
281
|
+
let debugObject;
|
|
262
282
|
if (debugOverlay)
|
|
263
283
|
{
|
|
264
284
|
const saveContext = mainContext;
|
|
@@ -269,11 +289,13 @@ function debugRender()
|
|
|
269
289
|
debugRect(cameraPos, cameraSize.subtract(vec2(.1)), '#f008');
|
|
270
290
|
|
|
271
291
|
// mouse pick
|
|
272
|
-
let bestDistance = Infinity
|
|
292
|
+
let bestDistance = Infinity;
|
|
273
293
|
for (const o of engineObjects)
|
|
274
294
|
{
|
|
275
295
|
if (o.canvas || o.destroyed)
|
|
276
296
|
continue;
|
|
297
|
+
|
|
298
|
+
o.renderDebugInfo();
|
|
277
299
|
if (!o.size.x || !o.size.y)
|
|
278
300
|
continue;
|
|
279
301
|
|
|
@@ -281,33 +303,15 @@ function debugRender()
|
|
|
281
303
|
if (distance < bestDistance)
|
|
282
304
|
{
|
|
283
305
|
bestDistance = distance;
|
|
284
|
-
|
|
306
|
+
debugObject = o;
|
|
285
307
|
}
|
|
286
|
-
|
|
287
|
-
// show object info
|
|
288
|
-
const size = vec2(max(o.size.x, .2), max(o.size.y, .2));
|
|
289
|
-
const color1 = new Color(o.collideTiles?1:0, o.collideSolidObjects?1:0, o.isSolid?1:0, o.parent?.2:.5);
|
|
290
|
-
const color2 = o.parent ? new Color(1,1,1,.5) : new Color(0,0,0,.8);
|
|
291
|
-
drawRect(o.pos, size, color1, o.angle, false);
|
|
292
|
-
drawRect(o.pos, size.scale(.8), color2, o.angle, false);
|
|
293
|
-
o.parent && drawLine(o.pos, o.parent.pos, .1, new Color(0,0,1,.5), false);
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
if (bestObject)
|
|
297
|
-
{
|
|
298
|
-
const raycastHitPos = tileCollisionRaycast(bestObject.pos, mousePos);
|
|
299
|
-
raycastHitPos && drawRect(raycastHitPos.floor().add(vec2(.5)), vec2(1), new Color(0,1,1,.3));
|
|
300
|
-
drawRect(mousePos.floor().add(vec2(.5)), vec2(1), new Color(0,0,1,.5), 0, false);
|
|
301
|
-
drawLine(mousePos, bestObject.pos, .1, raycastHitPos ? new Color(1,0,0,.5) : new Color(0,1,0,.5), false);
|
|
302
|
-
|
|
303
|
-
const debugText = 'mouse pos = ' + mousePos +
|
|
304
|
-
'\nmouse collision = ' + getTileCollisionData(mousePos) +
|
|
305
|
-
'\n\n--- object info ---\n' +
|
|
306
|
-
bestObject.toString();
|
|
307
|
-
drawTextScreen(debugText, mousePosScreen, 24, new Color, .05, undefined, 'center', 'monospace');
|
|
308
308
|
}
|
|
309
309
|
|
|
310
|
-
|
|
310
|
+
if (tileCollisionSize.x > 0 && tileCollisionSize.y > 0)
|
|
311
|
+
drawRect(mousePos.floor().add(vec2(.5)), vec2(1), rgb(0,0,1,.5), 0, false);
|
|
312
|
+
mainContext = saveContext;
|
|
313
|
+
|
|
314
|
+
//glCopyToContext(mainContext = saveContext);
|
|
311
315
|
}
|
|
312
316
|
|
|
313
317
|
{
|
|
@@ -322,6 +326,7 @@ function debugRender()
|
|
|
322
326
|
const pos = worldToScreen(p.pos);
|
|
323
327
|
overlayContext.translate(pos.x|0, pos.y|0);
|
|
324
328
|
overlayContext.rotate(p.angle);
|
|
329
|
+
overlayContext.scale(1, -1);
|
|
325
330
|
overlayContext.fillStyle = overlayContext.strokeStyle = p.color;
|
|
326
331
|
|
|
327
332
|
if (p.text != undefined)
|
|
@@ -331,7 +336,20 @@ function debugRender()
|
|
|
331
336
|
overlayContext.textBaseline = 'middle';
|
|
332
337
|
overlayContext.fillText(p.text, 0, 0);
|
|
333
338
|
}
|
|
334
|
-
else if (p.
|
|
339
|
+
else if (p.points != undefined)
|
|
340
|
+
{
|
|
341
|
+
// poly
|
|
342
|
+
overlayContext.beginPath();
|
|
343
|
+
for (const point of p.points)
|
|
344
|
+
{
|
|
345
|
+
const p2 = point.scale(cameraScale).floor();
|
|
346
|
+
overlayContext.lineTo(p2.x, p2.y);
|
|
347
|
+
}
|
|
348
|
+
overlayContext.closePath();
|
|
349
|
+
p.fill && overlayContext.fill();
|
|
350
|
+
overlayContext.stroke();
|
|
351
|
+
}
|
|
352
|
+
else if (p.size == 0 || p.size.x === 0 && p.size.y === 0)
|
|
335
353
|
{
|
|
336
354
|
// point
|
|
337
355
|
overlayContext.fillRect(-pointSize/2, -1, pointSize, 3);
|
|
@@ -340,7 +358,8 @@ function debugRender()
|
|
|
340
358
|
else if (p.size.x != undefined)
|
|
341
359
|
{
|
|
342
360
|
// rect
|
|
343
|
-
const
|
|
361
|
+
const s = p.size.scale(cameraScale).floor();
|
|
362
|
+
const w = s.x, h = s.y;
|
|
344
363
|
p.fill && overlayContext.fillRect(-w/2|0, -h/2|0, w, h);
|
|
345
364
|
overlayContext.strokeRect(-w/2|0, -h/2|0, w, h);
|
|
346
365
|
}
|
|
@@ -359,6 +378,22 @@ function debugRender()
|
|
|
359
378
|
// remove expired primitives
|
|
360
379
|
debugPrimitives = debugPrimitives.filter(r=>r.time<0);
|
|
361
380
|
}
|
|
381
|
+
|
|
382
|
+
if (debugObject)
|
|
383
|
+
{
|
|
384
|
+
const saveContext = mainContext;
|
|
385
|
+
mainContext = overlayContext;
|
|
386
|
+
const raycastHitPos = tileCollisionRaycast(debugObject.pos, mousePos);
|
|
387
|
+
raycastHitPos && drawRect(raycastHitPos.floor().add(vec2(.5)), vec2(1), rgb(0,1,1,.3));
|
|
388
|
+
drawLine(mousePos, debugObject.pos, .1, raycastHitPos ? rgb(1,0,0,.5) : rgb(0,1,0,.5), false);
|
|
389
|
+
|
|
390
|
+
const debugText = 'mouse pos = ' + mousePos +
|
|
391
|
+
'\nmouse collision = ' + getTileCollisionData(mousePos) +
|
|
392
|
+
'\n\n--- object info ---\n' +
|
|
393
|
+
debugObject.toString();
|
|
394
|
+
drawTextScreen(debugText, mousePosScreen, 24, rgb(), .05, undefined, 'center', 'monospace');
|
|
395
|
+
mainContext = saveContext;
|
|
396
|
+
}
|
|
362
397
|
|
|
363
398
|
{
|
|
364
399
|
// draw debug overlay
|
|
@@ -721,7 +756,7 @@ class RandomGenerator
|
|
|
721
756
|
*/
|
|
722
757
|
function vec2(x=0, y)
|
|
723
758
|
{
|
|
724
|
-
return typeof x
|
|
759
|
+
return typeof x == 'number' ?
|
|
725
760
|
new Vector2(x, y == undefined? x : y) :
|
|
726
761
|
new Vector2(x.x, x.y);
|
|
727
762
|
}
|
|
@@ -750,13 +785,19 @@ class Vector2
|
|
|
750
785
|
* @param {Number} [y] - Y axis location */
|
|
751
786
|
constructor(x=0, y=0)
|
|
752
787
|
{
|
|
753
|
-
ASSERT(typeof x
|
|
788
|
+
ASSERT(typeof x == 'number' && typeof y == 'number');
|
|
754
789
|
/** @property {Number} - X axis location */
|
|
755
790
|
this.x = x;
|
|
756
791
|
/** @property {Number} - Y axis location */
|
|
757
792
|
this.y = y;
|
|
758
793
|
}
|
|
759
794
|
|
|
795
|
+
/** Sets values of this vector and returns self
|
|
796
|
+
* @param {Number} [x] - X axis location
|
|
797
|
+
* @param {Number} [y] - Y axis location
|
|
798
|
+
* @return {Vector2} */
|
|
799
|
+
set(x=0, y=0) { this.x=x; this.y=y; return this; }
|
|
800
|
+
|
|
760
801
|
/** Returns a new vector that is a copy of this
|
|
761
802
|
* @return {Vector2} */
|
|
762
803
|
copy() { return new Vector2(this.x, this.y); }
|
|
@@ -1008,6 +1049,15 @@ class Color
|
|
|
1008
1049
|
this.a = a;
|
|
1009
1050
|
}
|
|
1010
1051
|
|
|
1052
|
+
/** Sets values of this color and returns self
|
|
1053
|
+
* @param {Number} [r] - red
|
|
1054
|
+
* @param {Number} [g] - green
|
|
1055
|
+
* @param {Number} [b] - blue
|
|
1056
|
+
* @param {Number} [a] - alpha
|
|
1057
|
+
* @return {Color} */
|
|
1058
|
+
set(r=1, g=1, b=1, a=1)
|
|
1059
|
+
{ this.r=r; this.g=g; this.b=b; this.a=a; return this; }
|
|
1060
|
+
|
|
1011
1061
|
/** Returns a new color that is a copy of this
|
|
1012
1062
|
* @return {Color} */
|
|
1013
1063
|
copy() { return new Color(this.r, this.g, this.b, this.a); }
|
|
@@ -1169,6 +1219,64 @@ class Color
|
|
|
1169
1219
|
}
|
|
1170
1220
|
}
|
|
1171
1221
|
|
|
1222
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
1223
|
+
// default colors
|
|
1224
|
+
|
|
1225
|
+
/** Color - White
|
|
1226
|
+
* @type {Color}
|
|
1227
|
+
* @memberof Utilities */
|
|
1228
|
+
const WHITE = rgb();
|
|
1229
|
+
|
|
1230
|
+
/** Color - Black
|
|
1231
|
+
* @type {Color}
|
|
1232
|
+
* @memberof Utilities */
|
|
1233
|
+
const BLACK = rgb(0,0,0);
|
|
1234
|
+
|
|
1235
|
+
/** Color - Gray
|
|
1236
|
+
* @type {Color}
|
|
1237
|
+
* @memberof Utilities */
|
|
1238
|
+
const GRAY = rgb(.5,.5,.5);
|
|
1239
|
+
|
|
1240
|
+
/** Color - Red
|
|
1241
|
+
* @type {Color}
|
|
1242
|
+
* @memberof Utilities */
|
|
1243
|
+
const RED = rgb(1,0,0);
|
|
1244
|
+
|
|
1245
|
+
/** Color - Orange
|
|
1246
|
+
* @type {Color}
|
|
1247
|
+
* @memberof Utilities */
|
|
1248
|
+
const ORANGE = rgb(1,.5,0);
|
|
1249
|
+
|
|
1250
|
+
/** Color - Yellow
|
|
1251
|
+
* @type {Color}
|
|
1252
|
+
* @memberof Utilities */
|
|
1253
|
+
const YELLOW = rgb(1,1,0);
|
|
1254
|
+
|
|
1255
|
+
/** Color - Green
|
|
1256
|
+
* @type {Color}
|
|
1257
|
+
* @memberof Utilities */
|
|
1258
|
+
const GREEN = rgb(0,1,0);
|
|
1259
|
+
|
|
1260
|
+
/** Color - Cyan
|
|
1261
|
+
* @type {Color}
|
|
1262
|
+
* @memberof Utilities */
|
|
1263
|
+
const CYAN = rgb(0,1,1);
|
|
1264
|
+
|
|
1265
|
+
/** Color - Blue
|
|
1266
|
+
* @type {Color}
|
|
1267
|
+
* @memberof Utilities */
|
|
1268
|
+
const BLUE = rgb(0,0,1);
|
|
1269
|
+
|
|
1270
|
+
/** Color - Purple
|
|
1271
|
+
* @type {Color}
|
|
1272
|
+
* @memberof Utilities */
|
|
1273
|
+
const PURPLE = rgb(.5,0,1);
|
|
1274
|
+
|
|
1275
|
+
/** Color - Magenta
|
|
1276
|
+
* @type {Color}
|
|
1277
|
+
* @memberof Utilities */
|
|
1278
|
+
const MAGENTA = rgb(1,0,1);
|
|
1279
|
+
|
|
1172
1280
|
///////////////////////////////////////////////////////////////////////////////
|
|
1173
1281
|
|
|
1174
1282
|
/**
|
|
@@ -1249,9 +1357,9 @@ let cameraScale = 32;
|
|
|
1249
1357
|
|
|
1250
1358
|
/** The max size of the canvas, centered if window is larger
|
|
1251
1359
|
* @type {Vector2}
|
|
1252
|
-
* @default Vector2(1920,
|
|
1360
|
+
* @default Vector2(1920,1080)
|
|
1253
1361
|
* @memberof Settings */
|
|
1254
|
-
let canvasMaxSize = vec2(1920,
|
|
1362
|
+
let canvasMaxSize = vec2(1920, 1080);
|
|
1255
1363
|
|
|
1256
1364
|
/** Fixed size of the canvas, if enabled canvas size never changes
|
|
1257
1365
|
* - you may also need to set mainCanvasSize if using screen space coords in startup
|
|
@@ -1898,7 +2006,7 @@ class EngineObject
|
|
|
1898
2006
|
if (o.mass) // push away if not fixed
|
|
1899
2007
|
o.velocity = o.velocity.subtract(velocity);
|
|
1900
2008
|
|
|
1901
|
-
debugOverlay && debugPhysics &&
|
|
2009
|
+
debugOverlay && debugPhysics && debugOverlap(this.pos, this.size, o.pos, o.size, '#f00');
|
|
1902
2010
|
continue;
|
|
1903
2011
|
}
|
|
1904
2012
|
|
|
@@ -1960,7 +2068,7 @@ class EngineObject
|
|
|
1960
2068
|
else // bounce if other object is fixed
|
|
1961
2069
|
this.velocity.x *= -elasticity;
|
|
1962
2070
|
}
|
|
1963
|
-
debugOverlay && debugPhysics &&
|
|
2071
|
+
debugOverlay && debugPhysics && debugOverlap(this.pos, this.size, o.pos, o.size, '#f0f');
|
|
1964
2072
|
}
|
|
1965
2073
|
}
|
|
1966
2074
|
if (this.collideTiles)
|
|
@@ -2021,6 +2129,22 @@ class EngineObject
|
|
|
2021
2129
|
for (const child of this.children)
|
|
2022
2130
|
child.destroy(child.parent = 0);
|
|
2023
2131
|
}
|
|
2132
|
+
|
|
2133
|
+
/** Convert from local space to world space
|
|
2134
|
+
* @param {Vector2} pos - local space point */
|
|
2135
|
+
localToWorld(pos) { return this.pos.add(pos.rotate(-this.angle)); }
|
|
2136
|
+
|
|
2137
|
+
/** Convert from world space to local space
|
|
2138
|
+
* @param {Vector2} pos - world space point */
|
|
2139
|
+
worldToLocal(pos) { return pos.subtract(this.pos).rotate(this.angle); }
|
|
2140
|
+
|
|
2141
|
+
/** Convert from local space to world space for a vector (rotation only)
|
|
2142
|
+
* @param {Vector2} vec - local space vector */
|
|
2143
|
+
localToWorldVector(vec) { return vec.rotate(this.angle); }
|
|
2144
|
+
|
|
2145
|
+
/** Convert from world space to local space for a vector (rotation only)
|
|
2146
|
+
* @param {Vector2} vec - world space vector */
|
|
2147
|
+
worldToLocalVector(vec) { return vec.rotate(-this.angle); }
|
|
2024
2148
|
|
|
2025
2149
|
/** Called to check if a tile collision should be resolved
|
|
2026
2150
|
* @param {Number} tileData - the value of the tile at the position
|
|
@@ -2107,6 +2231,18 @@ class EngineObject
|
|
|
2107
2231
|
return text;
|
|
2108
2232
|
}
|
|
2109
2233
|
}
|
|
2234
|
+
|
|
2235
|
+
/** Render debug info for this object */
|
|
2236
|
+
renderDebugInfo()
|
|
2237
|
+
{
|
|
2238
|
+
// show object info for debugging
|
|
2239
|
+
const size = vec2(max(this.size.x, .2), max(this.size.y, .2));
|
|
2240
|
+
const color1 = rgb(this.collideTiles?1:0, this.collideSolidObjects?1:0, this.isSolid?1:0, this.parent?.2:.5);
|
|
2241
|
+
const color2 = this.parent ? rgb(1,1,1,.5) : rgb(0,0,0,.8);
|
|
2242
|
+
drawRect(this.pos, size, color1, this.angle, false);
|
|
2243
|
+
drawRect(this.pos, size.scale(.8), color2, this.angle, false);
|
|
2244
|
+
this.parent && drawLine(this.pos, this.parent.pos, .1, rgb(0,0,1,.5), false);
|
|
2245
|
+
}
|
|
2110
2246
|
}
|
|
2111
2247
|
/**
|
|
2112
2248
|
* LittleJS Drawing System
|
|
@@ -2430,7 +2566,7 @@ function drawCanvas2D(pos, size, angle, mirror, drawFunction, screenSpace, conte
|
|
|
2430
2566
|
context.save();
|
|
2431
2567
|
context.translate(pos.x+.5, pos.y+.5);
|
|
2432
2568
|
context.rotate(angle);
|
|
2433
|
-
context.scale(mirror ? -size.x : size.x, size.y);
|
|
2569
|
+
context.scale(mirror ? -size.x : size.x, -size.y);
|
|
2434
2570
|
drawFunction(context);
|
|
2435
2571
|
context.restore();
|
|
2436
2572
|
}
|
|
@@ -3226,7 +3362,17 @@ class Sound
|
|
|
3226
3362
|
|
|
3227
3363
|
// play the sound
|
|
3228
3364
|
const playbackRate = pitch + pitch * this.randomness*randomnessScale*rand(-1,1);
|
|
3229
|
-
|
|
3365
|
+
this.gainNode = audioContext.createGain();
|
|
3366
|
+
return this.source = playSamples(this.sampleChannels, volume, playbackRate, pan, loop, this.sampleRate, this.gainNode);
|
|
3367
|
+
}
|
|
3368
|
+
|
|
3369
|
+
/** Set the sound volume
|
|
3370
|
+
* @param {Number} [volume] - How much to scale volume by
|
|
3371
|
+
*/
|
|
3372
|
+
setVolume(volume=1)
|
|
3373
|
+
{
|
|
3374
|
+
if (this.gainNode)
|
|
3375
|
+
this.gainNode.gain.value = volume;
|
|
3230
3376
|
}
|
|
3231
3377
|
|
|
3232
3378
|
/** Stop the last instance of this sound that was played */
|
|
@@ -3414,15 +3560,16 @@ function getNoteFrequency(semitoneOffset, rootFrequency=220)
|
|
|
3414
3560
|
let audioSuspended = false;
|
|
3415
3561
|
|
|
3416
3562
|
/** Play cached audio samples with given settings
|
|
3417
|
-
* @param {Array}
|
|
3418
|
-
* @param {Number}
|
|
3419
|
-
* @param {Number}
|
|
3420
|
-
* @param {Number}
|
|
3421
|
-
* @param {Boolean}
|
|
3422
|
-
* @param {Number}
|
|
3563
|
+
* @param {Array} sampleChannels - Array of arrays of samples to play (for stereo playback)
|
|
3564
|
+
* @param {Number} [volume] - How much to scale volume by
|
|
3565
|
+
* @param {Number} [rate] - The playback rate to use
|
|
3566
|
+
* @param {Number} [pan] - How much to apply stereo panning
|
|
3567
|
+
* @param {Boolean} [loop] - True if the sound should loop when it reaches the end
|
|
3568
|
+
* @param {Number} [sampleRate=44100] - Sample rate for the sound
|
|
3569
|
+
* @param {GainNode} [gainNode] - Optional gain node for volume control while playing
|
|
3423
3570
|
* @return {AudioBufferSourceNode} - The audio node of the sound played
|
|
3424
3571
|
* @memberof Audio */
|
|
3425
|
-
function playSamples(sampleChannels, volume=1, rate=1, pan=0, loop=false, sampleRate=zzfxR)
|
|
3572
|
+
function playSamples(sampleChannels, volume=1, rate=1, pan=0, loop=false, sampleRate=zzfxR, gainNode)
|
|
3426
3573
|
{
|
|
3427
3574
|
if (!soundEnable || headlessMode) return;
|
|
3428
3575
|
|
|
@@ -3448,11 +3595,8 @@ function playSamples(sampleChannels, volume=1, rate=1, pan=0, loop=false, sample
|
|
|
3448
3595
|
source.playbackRate.value = rate;
|
|
3449
3596
|
source.loop = loop;
|
|
3450
3597
|
|
|
3451
|
-
// set master gain volume
|
|
3452
|
-
setSoundVolume(soundVolume);
|
|
3453
|
-
|
|
3454
3598
|
// create and connect gain node
|
|
3455
|
-
|
|
3599
|
+
gainNode = gainNode || audioContext.createGain();
|
|
3456
3600
|
gainNode.gain.value = volume;
|
|
3457
3601
|
gainNode.connect(audioGainNode);
|
|
3458
3602
|
|
|
@@ -4433,9 +4577,9 @@ class Particle extends EngineObject
|
|
|
4433
4577
|
|
|
4434
4578
|
|
|
4435
4579
|
/** List of all medals
|
|
4436
|
-
* @type {
|
|
4580
|
+
* @type {Object}
|
|
4437
4581
|
* @memberof Medals */
|
|
4438
|
-
const medals =
|
|
4582
|
+
const medals = {};
|
|
4439
4583
|
|
|
4440
4584
|
// Engine internal variables not exposed to documentation
|
|
4441
4585
|
let medalsDisplayQueue = [], medalsSaveName, medalsDisplayTimeLast;
|
|
@@ -4451,9 +4595,45 @@ function medalsInit(saveName)
|
|
|
4451
4595
|
{
|
|
4452
4596
|
// check if medals are unlocked
|
|
4453
4597
|
medalsSaveName = saveName;
|
|
4454
|
-
|
|
4598
|
+
if (!debugMedals)
|
|
4599
|
+
medalsForEach(medal=> medal.unlocked = (localStorage[medal.storageKey()] | 0));
|
|
4600
|
+
|
|
4601
|
+
// engine automatically renders medals
|
|
4602
|
+
addPluginRender(function()
|
|
4603
|
+
{
|
|
4604
|
+
if (!medalsDisplayQueue.length)
|
|
4605
|
+
return;
|
|
4606
|
+
|
|
4607
|
+
// update first medal in queue
|
|
4608
|
+
const medal = medalsDisplayQueue[0];
|
|
4609
|
+
const time = timeReal - medalsDisplayTimeLast;
|
|
4610
|
+
if (!medalsDisplayTimeLast)
|
|
4611
|
+
medalsDisplayTimeLast = timeReal;
|
|
4612
|
+
else if (time > medalDisplayTime)
|
|
4613
|
+
{
|
|
4614
|
+
medalsDisplayTimeLast = 0;
|
|
4615
|
+
medalsDisplayQueue.shift();
|
|
4616
|
+
}
|
|
4617
|
+
else
|
|
4618
|
+
{
|
|
4619
|
+
// slide on/off medals
|
|
4620
|
+
const slideOffTime = medalDisplayTime - medalDisplaySlideTime;
|
|
4621
|
+
const hidePercent =
|
|
4622
|
+
time < medalDisplaySlideTime ? 1 - time / medalDisplaySlideTime :
|
|
4623
|
+
time > slideOffTime ? (time - slideOffTime) / medalDisplaySlideTime : 0;
|
|
4624
|
+
medal.render(hidePercent);
|
|
4625
|
+
}
|
|
4626
|
+
});
|
|
4455
4627
|
}
|
|
4456
4628
|
|
|
4629
|
+
/** Calls a function for each medal
|
|
4630
|
+
* @param {Function} callback
|
|
4631
|
+
* @memberof Medals */
|
|
4632
|
+
function medalsForEach(callback)
|
|
4633
|
+
{ Object.values(medals).forEach(medal=>callback(medal)); }
|
|
4634
|
+
|
|
4635
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
4636
|
+
|
|
4457
4637
|
/**
|
|
4458
4638
|
* Medal - Tracks an unlockable medal
|
|
4459
4639
|
* @example
|
|
@@ -4498,7 +4678,6 @@ class Medal
|
|
|
4498
4678
|
ASSERT(medalsSaveName, 'save name must be set');
|
|
4499
4679
|
localStorage[this.storageKey()] = this.unlocked = 1;
|
|
4500
4680
|
medalsDisplayQueue.push(this);
|
|
4501
|
-
newgrounds && newgrounds.unlockMedal(this.id);
|
|
4502
4681
|
}
|
|
4503
4682
|
|
|
4504
4683
|
/** Render a medal
|
|
@@ -4546,173 +4725,6 @@ class Medal
|
|
|
4546
4725
|
|
|
4547
4726
|
// Get local storage key used by the medal
|
|
4548
4727
|
storageKey() { return medalsSaveName + '_' + this.id; }
|
|
4549
|
-
}
|
|
4550
|
-
|
|
4551
|
-
// engine automatically renders medals
|
|
4552
|
-
function medalsRender()
|
|
4553
|
-
{
|
|
4554
|
-
if (!medalsDisplayQueue.length)
|
|
4555
|
-
return;
|
|
4556
|
-
|
|
4557
|
-
// update first medal in queue
|
|
4558
|
-
const medal = medalsDisplayQueue[0];
|
|
4559
|
-
const time = timeReal - medalsDisplayTimeLast;
|
|
4560
|
-
if (!medalsDisplayTimeLast)
|
|
4561
|
-
medalsDisplayTimeLast = timeReal;
|
|
4562
|
-
else if (time > medalDisplayTime)
|
|
4563
|
-
{
|
|
4564
|
-
medalsDisplayTimeLast = 0;
|
|
4565
|
-
medalsDisplayQueue.shift();
|
|
4566
|
-
}
|
|
4567
|
-
else
|
|
4568
|
-
{
|
|
4569
|
-
// slide on/off medals
|
|
4570
|
-
const slideOffTime = medalDisplayTime - medalDisplaySlideTime;
|
|
4571
|
-
const hidePercent =
|
|
4572
|
-
time < medalDisplaySlideTime ? 1 - time / medalDisplaySlideTime :
|
|
4573
|
-
time > slideOffTime ? (time - slideOffTime) / medalDisplaySlideTime : 0;
|
|
4574
|
-
medal.render(hidePercent);
|
|
4575
|
-
}
|
|
4576
|
-
}
|
|
4577
|
-
|
|
4578
|
-
///////////////////////////////////////////////////////////////////////////////
|
|
4579
|
-
|
|
4580
|
-
// global Newgrounds object
|
|
4581
|
-
let newgrounds;
|
|
4582
|
-
|
|
4583
|
-
/** This can used to enable Newgrounds functionality
|
|
4584
|
-
* @param {Number} app_id - The newgrounds App ID
|
|
4585
|
-
* @param {String} [cipher] - The encryption Key (AES-128/Base64)
|
|
4586
|
-
* @param {Object} [cryptoJS] - An instance of CryptoJS, if there is a cipher
|
|
4587
|
-
* @memberof Medals */
|
|
4588
|
-
function newgroundsInit(app_id, cipher, cryptoJS)
|
|
4589
|
-
{ newgrounds = new Newgrounds(app_id, cipher, cryptoJS); }
|
|
4590
|
-
|
|
4591
|
-
/**
|
|
4592
|
-
* Newgrounds API wrapper object
|
|
4593
|
-
* @example
|
|
4594
|
-
* // create a newgrounds object, replace the app id with your own
|
|
4595
|
-
* const app_id = '53123:1ZuSTQ9l';
|
|
4596
|
-
* newgrounds = new Newgrounds(app_id);
|
|
4597
|
-
*/
|
|
4598
|
-
class Newgrounds
|
|
4599
|
-
{
|
|
4600
|
-
/** Create a newgrounds object
|
|
4601
|
-
* @param {Number} app_id - The newgrounds App ID
|
|
4602
|
-
* @param {String} [cipher] - The encryption Key (AES-128/Base64)
|
|
4603
|
-
* @param {Object} [cryptoJS] - An instance of CryptoJS, if there is a cipher */
|
|
4604
|
-
constructor(app_id, cipher, cryptoJS)
|
|
4605
|
-
{
|
|
4606
|
-
ASSERT(!newgrounds && app_id>0, 'there can only be one newgrounds object');
|
|
4607
|
-
ASSERT(!cipher || cryptoJS, 'must provide cryptojs if there is a cipher');
|
|
4608
|
-
|
|
4609
|
-
this.app_id = app_id;
|
|
4610
|
-
this.cipher = cipher;
|
|
4611
|
-
this.cryptoJS = cryptoJS;
|
|
4612
|
-
this.host = location ? location.hostname : '';
|
|
4613
|
-
|
|
4614
|
-
// get session id from url search params
|
|
4615
|
-
const url = new URL(location.href);
|
|
4616
|
-
this.session_id = url.searchParams.get('ngio_session_id');
|
|
4617
|
-
|
|
4618
|
-
if (!this.session_id)
|
|
4619
|
-
return; // only use newgrounds when logged in
|
|
4620
|
-
|
|
4621
|
-
// get medals
|
|
4622
|
-
const medalsResult = this.call('Medal.getList');
|
|
4623
|
-
this.medals = medalsResult ? medalsResult.result.data['medals'] : [];
|
|
4624
|
-
debugMedals && console.log(this.medals);
|
|
4625
|
-
for (const newgroundsMedal of this.medals)
|
|
4626
|
-
{
|
|
4627
|
-
const medal = medals[newgroundsMedal['id']];
|
|
4628
|
-
if (medal)
|
|
4629
|
-
{
|
|
4630
|
-
// copy newgrounds medal data
|
|
4631
|
-
medal.image = new Image;
|
|
4632
|
-
medal.image.src = newgroundsMedal['icon'];
|
|
4633
|
-
medal.name = newgroundsMedal['name'];
|
|
4634
|
-
medal.description = newgroundsMedal['description'];
|
|
4635
|
-
medal.unlocked = newgroundsMedal['unlocked'];
|
|
4636
|
-
medal.difficulty = newgroundsMedal['difficulty'];
|
|
4637
|
-
medal.value = newgroundsMedal['value'];
|
|
4638
|
-
|
|
4639
|
-
if (medal.value)
|
|
4640
|
-
medal.description = medal.description + ' (' + medal.value + ')';
|
|
4641
|
-
}
|
|
4642
|
-
}
|
|
4643
|
-
|
|
4644
|
-
// get scoreboards
|
|
4645
|
-
const scoreboardResult = this.call('ScoreBoard.getBoards');
|
|
4646
|
-
this.scoreboards = scoreboardResult ? scoreboardResult.result.data.scoreboards : [];
|
|
4647
|
-
debugMedals && console.log(this.scoreboards);
|
|
4648
|
-
|
|
4649
|
-
const keepAliveMS = 5 * 60 * 1e3;
|
|
4650
|
-
setInterval(()=>this.call('Gateway.ping', 0, true), keepAliveMS);
|
|
4651
|
-
}
|
|
4652
|
-
|
|
4653
|
-
/** Send message to unlock a medal by id
|
|
4654
|
-
* @param {Number} id - The medal id */
|
|
4655
|
-
unlockMedal(id) { return this.call('Medal.unlock', {'id':id}, true); }
|
|
4656
|
-
|
|
4657
|
-
/** Send message to post score
|
|
4658
|
-
* @param {Number} id - The scoreboard id
|
|
4659
|
-
* @param {Number} value - The score value */
|
|
4660
|
-
postScore(id, value) { return this.call('ScoreBoard.postScore', {'id':id, 'value':value}, true); }
|
|
4661
|
-
|
|
4662
|
-
/** Get scores from a scoreboard
|
|
4663
|
-
* @param {Number} id - The scoreboard id
|
|
4664
|
-
* @param {String} [user] - A user's id or name
|
|
4665
|
-
* @param {Number} [social] - If true, only social scores will be loaded
|
|
4666
|
-
* @param {Number} [skip] - Number of scores to skip before start
|
|
4667
|
-
* @param {Number} [limit] - Number of scores to include in the list
|
|
4668
|
-
* @return {Object} - The response JSON object
|
|
4669
|
-
*/
|
|
4670
|
-
getScores(id, user, social=0, skip=0, limit=10)
|
|
4671
|
-
{ return this.call('ScoreBoard.getScores', {'id':id, 'user':user, 'social':social, 'skip':skip, 'limit':limit}); }
|
|
4672
|
-
|
|
4673
|
-
/** Send message to log a view */
|
|
4674
|
-
logView() { return this.call('App.logView', {'host':this.host}, true); }
|
|
4675
|
-
|
|
4676
|
-
/** Send a message to call a component of the Newgrounds API
|
|
4677
|
-
* @param {String} component - Name of the component
|
|
4678
|
-
* @param {Object} [parameters] - Parameters to use for call
|
|
4679
|
-
* @param {Boolean} [async] - If true, don't wait for response before continuing
|
|
4680
|
-
* @return {Object} - The response JSON object
|
|
4681
|
-
*/
|
|
4682
|
-
call(component, parameters, async=false)
|
|
4683
|
-
{
|
|
4684
|
-
const call = {'component':component, 'parameters':parameters};
|
|
4685
|
-
if (this.cipher)
|
|
4686
|
-
{
|
|
4687
|
-
// encrypt using AES-128 Base64 with cryptoJS
|
|
4688
|
-
const cryptoJS = this.cryptoJS;
|
|
4689
|
-
const aesKey = cryptoJS['enc']['Base64']['parse'](this.cipher);
|
|
4690
|
-
const iv = cryptoJS['lib']['WordArray']['random'](16);
|
|
4691
|
-
const encrypted = cryptoJS['AES']['encrypt'](JSON.stringify(call), aesKey, {'iv':iv});
|
|
4692
|
-
call['secure'] = cryptoJS['enc']['Base64']['stringify'](iv.concat(encrypted['ciphertext']));
|
|
4693
|
-
call['parameters'] = 0;
|
|
4694
|
-
}
|
|
4695
|
-
|
|
4696
|
-
// build the input object
|
|
4697
|
-
const input =
|
|
4698
|
-
{
|
|
4699
|
-
'app_id': this.app_id,
|
|
4700
|
-
'session_id': this.session_id,
|
|
4701
|
-
'call': call
|
|
4702
|
-
};
|
|
4703
|
-
|
|
4704
|
-
// build post data
|
|
4705
|
-
const formData = new FormData();
|
|
4706
|
-
formData.append('input', JSON.stringify(input));
|
|
4707
|
-
|
|
4708
|
-
// send post data
|
|
4709
|
-
const xmlHttp = new XMLHttpRequest();
|
|
4710
|
-
const url = 'https://newgrounds.io/gateway_v3.php';
|
|
4711
|
-
xmlHttp.open('POST', url, !debugMedals && async);
|
|
4712
|
-
xmlHttp.send(formData);
|
|
4713
|
-
debugMedals && console.log(xmlHttp.responseText);
|
|
4714
|
-
return xmlHttp.responseText && JSON.parse(xmlHttp.responseText);
|
|
4715
|
-
}
|
|
4716
4728
|
}
|
|
4717
4729
|
/**
|
|
4718
4730
|
* LittleJS WebGL Interface
|
|
@@ -4803,7 +4815,7 @@ function glPreRender()
|
|
|
4803
4815
|
|
|
4804
4816
|
// clear and set to same size as main canvas
|
|
4805
4817
|
glContext.viewport(0, 0, glCanvas.width=mainCanvas.width, glCanvas.height=mainCanvas.height);
|
|
4806
|
-
|
|
4818
|
+
glContext.clear(gl_COLOR_BUFFER_BIT);
|
|
4807
4819
|
|
|
4808
4820
|
// set up the shader
|
|
4809
4821
|
glContext.useProgram(glShader);
|
|
@@ -4986,99 +4998,6 @@ function glDraw(x, y, sizeX, sizeY, angle, uv0X, uv0Y, uv1X, uv1Y, rgba, rgbaAdd
|
|
|
4986
4998
|
glInstanceCount++;
|
|
4987
4999
|
}
|
|
4988
5000
|
|
|
4989
|
-
///////////////////////////////////////////////////////////////////////////////
|
|
4990
|
-
// post processing - can be enabled to pass other canvases through a final shader
|
|
4991
|
-
|
|
4992
|
-
let glPostShader, glPostTexture, glPostIncludeOverlay;
|
|
4993
|
-
|
|
4994
|
-
/** Set up a post processing shader
|
|
4995
|
-
* @param {String} shaderCode
|
|
4996
|
-
* @param {Boolean} includeOverlay
|
|
4997
|
-
* @memberof WebGL */
|
|
4998
|
-
function glInitPostProcess(shaderCode, includeOverlay=false)
|
|
4999
|
-
{
|
|
5000
|
-
ASSERT(!glPostShader, 'can only have 1 post effects shader');
|
|
5001
|
-
if (headlessMode) return;
|
|
5002
|
-
if (!shaderCode) // default shader pass through
|
|
5003
|
-
shaderCode = 'void mainImage(out vec4 c,vec2 p){c=texture(iChannel0,p/iResolution.xy);}';
|
|
5004
|
-
|
|
5005
|
-
// create the shader
|
|
5006
|
-
glPostShader = glCreateProgram(
|
|
5007
|
-
'#version 300 es\n' + // specify GLSL ES version
|
|
5008
|
-
'precision highp float;'+ // use highp for better accuracy
|
|
5009
|
-
'in vec2 p;'+ // position
|
|
5010
|
-
'void main(){'+ // shader entry point
|
|
5011
|
-
'gl_Position=vec4(p+p-1.,1,1);'+ // set position
|
|
5012
|
-
'}' // end of shader
|
|
5013
|
-
,
|
|
5014
|
-
'#version 300 es\n' + // specify GLSL ES version
|
|
5015
|
-
'precision highp float;'+ // use highp for better accuracy
|
|
5016
|
-
'uniform sampler2D iChannel0;'+ // input texture
|
|
5017
|
-
'uniform vec3 iResolution;'+ // size of output texture
|
|
5018
|
-
'uniform float iTime;'+ // time
|
|
5019
|
-
'out vec4 c;'+ // out color
|
|
5020
|
-
'\n' + shaderCode + '\n'+ // insert custom shader code
|
|
5021
|
-
'void main(){'+ // shader entry point
|
|
5022
|
-
'mainImage(c,gl_FragCoord.xy);'+ // call post process function
|
|
5023
|
-
'c.a=1.;'+ // always use full alpha
|
|
5024
|
-
'}' // end of shader
|
|
5025
|
-
);
|
|
5026
|
-
|
|
5027
|
-
// create buffer and texture
|
|
5028
|
-
glPostTexture = glCreateTexture(undefined);
|
|
5029
|
-
glPostIncludeOverlay = includeOverlay;
|
|
5030
|
-
|
|
5031
|
-
// hide the original 2d canvas
|
|
5032
|
-
mainCanvas.style.visibility = 'hidden';
|
|
5033
|
-
if (glPostIncludeOverlay)
|
|
5034
|
-
overlayCanvas.style.visibility = 'hidden';
|
|
5035
|
-
}
|
|
5036
|
-
|
|
5037
|
-
// Render the post processing shader, called automatically by the engine
|
|
5038
|
-
function glRenderPostProcess()
|
|
5039
|
-
{
|
|
5040
|
-
if (!glPostShader || headlessMode) return;
|
|
5041
|
-
|
|
5042
|
-
// prepare to render post process shader
|
|
5043
|
-
if (glEnable)
|
|
5044
|
-
{
|
|
5045
|
-
glFlush(); // clear out the buffer
|
|
5046
|
-
mainContext.drawImage(glCanvas, 0, 0); // copy to the main canvas
|
|
5047
|
-
}
|
|
5048
|
-
else
|
|
5049
|
-
{
|
|
5050
|
-
// set the viewport
|
|
5051
|
-
glContext.viewport(0, 0, glCanvas.width = mainCanvas.width, glCanvas.height = mainCanvas.height);
|
|
5052
|
-
}
|
|
5053
|
-
|
|
5054
|
-
// copy overlay canvas so it will be included in post processing
|
|
5055
|
-
glPostIncludeOverlay && mainContext.drawImage(overlayCanvas, 0, 0);
|
|
5056
|
-
|
|
5057
|
-
// setup shader program to draw one triangle
|
|
5058
|
-
glContext.useProgram(glPostShader);
|
|
5059
|
-
glContext.bindBuffer(gl_ARRAY_BUFFER, glGeometryBuffer);
|
|
5060
|
-
glContext.pixelStorei(gl_UNPACK_FLIP_Y_WEBGL, 1);
|
|
5061
|
-
glContext.disable(gl_BLEND);
|
|
5062
|
-
|
|
5063
|
-
// set textures, pass in the 2d canvas and gl canvas in separate texture channels
|
|
5064
|
-
glContext.activeTexture(gl_TEXTURE0);
|
|
5065
|
-
glContext.bindTexture(gl_TEXTURE_2D, glPostTexture);
|
|
5066
|
-
glContext.texImage2D(gl_TEXTURE_2D, 0, gl_RGBA, gl_RGBA, gl_UNSIGNED_BYTE, mainCanvas);
|
|
5067
|
-
|
|
5068
|
-
// set vertex position attribute
|
|
5069
|
-
const vertexByteStride = 8;
|
|
5070
|
-
const pLocation = glContext.getAttribLocation(glPostShader, 'p');
|
|
5071
|
-
glContext.enableVertexAttribArray(pLocation);
|
|
5072
|
-
glContext.vertexAttribPointer(pLocation, 2, gl_FLOAT, false, vertexByteStride, 0);
|
|
5073
|
-
|
|
5074
|
-
// set uniforms and draw
|
|
5075
|
-
const uniformLocation = (name)=>glContext.getUniformLocation(glPostShader, name);
|
|
5076
|
-
glContext.uniform1i(uniformLocation('iChannel0'), 0);
|
|
5077
|
-
glContext.uniform1f(uniformLocation('iTime'), time);
|
|
5078
|
-
glContext.uniform3f(uniformLocation('iResolution'), mainCanvas.width, mainCanvas.height, 1);
|
|
5079
|
-
glContext.drawArrays(gl_TRIANGLE_STRIP, 0, 4);
|
|
5080
|
-
}
|
|
5081
|
-
|
|
5082
5001
|
///////////////////////////////////////////////////////////////////////////////
|
|
5083
5002
|
// store gl constants as integers so their name doesn't use space in minifed
|
|
5084
5003
|
const
|
|
@@ -5143,7 +5062,7 @@ const engineName = 'LittleJS';
|
|
|
5143
5062
|
* @type {String}
|
|
5144
5063
|
* @default
|
|
5145
5064
|
* @memberof Engine */
|
|
5146
|
-
const engineVersion = '1.9.
|
|
5065
|
+
const engineVersion = '1.9.8';
|
|
5147
5066
|
|
|
5148
5067
|
/** Frames per second to update
|
|
5149
5068
|
* @type {Number}
|
|
@@ -5197,6 +5116,22 @@ function setPaused(isPaused) { paused = isPaused; }
|
|
|
5197
5116
|
let frameTimeLastMS = 0, frameTimeBufferMS = 0, averageFPS = 0;
|
|
5198
5117
|
|
|
5199
5118
|
///////////////////////////////////////////////////////////////////////////////
|
|
5119
|
+
// plugin hooks
|
|
5120
|
+
|
|
5121
|
+
const pluginUpdateList = [], pluginRenderList = [];
|
|
5122
|
+
|
|
5123
|
+
/** Add a new update function for a plugin
|
|
5124
|
+
* @param {Function} updateFunction
|
|
5125
|
+
* @memberof Engine */
|
|
5126
|
+
function addPluginUpdate(updateFunction) { pluginUpdateList.push(updateFunction); }
|
|
5127
|
+
|
|
5128
|
+
/** Add a new render function for a plugin
|
|
5129
|
+
* @param {Function} renderFunction
|
|
5130
|
+
* @memberof Engine */
|
|
5131
|
+
function addPluginRender(renderFunction) { pluginRenderList.push(renderFunction); }
|
|
5132
|
+
|
|
5133
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
5134
|
+
// Main engine functions
|
|
5200
5135
|
|
|
5201
5136
|
/** Startup LittleJS engine with your callback functions
|
|
5202
5137
|
* @param {Function} gameInit - Called once after the engine starts up, setup the game
|
|
@@ -5272,6 +5207,7 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
5272
5207
|
// update game and objects
|
|
5273
5208
|
inputUpdate();
|
|
5274
5209
|
gameUpdate();
|
|
5210
|
+
pluginUpdateList.forEach(f=>f());
|
|
5275
5211
|
engineObjectsUpdate();
|
|
5276
5212
|
|
|
5277
5213
|
// do post update
|
|
@@ -5293,8 +5229,7 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
5293
5229
|
for (const o of engineObjects)
|
|
5294
5230
|
o.destroyed || o.render();
|
|
5295
5231
|
gameRenderPost();
|
|
5296
|
-
|
|
5297
|
-
medalsRender();
|
|
5232
|
+
pluginRenderList.forEach(f=>f());
|
|
5298
5233
|
touchGamepadRender();
|
|
5299
5234
|
debugRender();
|
|
5300
5235
|
glCopyToContext(mainContext);
|
|
@@ -5584,21 +5519,23 @@ function drawEngineSplashScreen(t)
|
|
|
5584
5519
|
x.setLineDash([99*p2,99]);
|
|
5585
5520
|
|
|
5586
5521
|
// cab top
|
|
5587
|
-
rect(7,
|
|
5588
|
-
rect(7,
|
|
5589
|
-
rect(25,
|
|
5590
|
-
rect(25,
|
|
5591
|
-
rect(25,
|
|
5522
|
+
rect(7,16,18,-8,color(2,2));
|
|
5523
|
+
rect(7,8,18,4,color(2,3));
|
|
5524
|
+
rect(25,8,8,8,color(2,1));
|
|
5525
|
+
rect(25,8,-18,8);
|
|
5526
|
+
rect(25,8,8,8);
|
|
5592
5527
|
|
|
5593
5528
|
// cab
|
|
5594
|
-
rect(25,
|
|
5595
|
-
rect(11,
|
|
5596
|
-
rect(11,
|
|
5597
|
-
rect(11,
|
|
5598
|
-
rect(
|
|
5599
|
-
|
|
5600
|
-
|
|
5601
|
-
rect(
|
|
5529
|
+
rect(25,16,7,23,color());
|
|
5530
|
+
rect(11,39,14,-23,color(1,1));
|
|
5531
|
+
rect(11,16,14,18,color(1,2));
|
|
5532
|
+
rect(11,16,14,8,color(1,3));
|
|
5533
|
+
rect(25,16,-14,24);
|
|
5534
|
+
|
|
5535
|
+
// cab window
|
|
5536
|
+
rect(15,29,6,-9,color(2,2));
|
|
5537
|
+
circle(15,21,5,0,PI/2,color(2,4),1);
|
|
5538
|
+
rect(21,21,-6,9);
|
|
5602
5539
|
|
|
5603
5540
|
// little stack
|
|
5604
5541
|
rect(37,14,9,6,color(3,2));
|
|
@@ -5606,16 +5543,16 @@ function drawEngineSplashScreen(t)
|
|
|
5606
5543
|
rect(37,14,9,6);
|
|
5607
5544
|
|
|
5608
5545
|
// big stack
|
|
5609
|
-
rect(50,20,10,-8,color(0,1))
|
|
5610
|
-
rect(50,20,6.5,-8,color(0,2))
|
|
5611
|
-
rect(50,20,3.5,-8,color(0,3))
|
|
5612
|
-
rect(50,20,10,-8)
|
|
5613
|
-
circle(55,2,11.4,.5,PI-.5,color(3,3))
|
|
5614
|
-
circle(55,2,11.4,.5,PI/2,color(3,2),1)
|
|
5615
|
-
circle(55,2,11.4,.5,PI-.5)
|
|
5616
|
-
rect(45,7,20,-7,color(0,2))
|
|
5617
|
-
rect(45,-1,20,4,color(0,3))
|
|
5618
|
-
rect(45,-1,20,8)
|
|
5546
|
+
rect(50,20,10,-8,color(0,1));
|
|
5547
|
+
rect(50,20,6.5,-8,color(0,2));
|
|
5548
|
+
rect(50,20,3.5,-8,color(0,3));
|
|
5549
|
+
rect(50,20,10,-8);
|
|
5550
|
+
circle(55,2,11.4,.5,PI-.5,color(3,3));
|
|
5551
|
+
circle(55,2,11.4,.5,PI/2,color(3,2),1);
|
|
5552
|
+
circle(55,2,11.4,.5,PI-.5);
|
|
5553
|
+
rect(45,7,20,-7,color(0,2));
|
|
5554
|
+
rect(45,-1,20,4,color(0,3));
|
|
5555
|
+
rect(45,-1,20,8);
|
|
5619
5556
|
|
|
5620
5557
|
// engine
|
|
5621
5558
|
for (let i=5; i--;)
|
|
@@ -5717,13 +5654,16 @@ export {
|
|
|
5717
5654
|
// Debug
|
|
5718
5655
|
ASSERT,
|
|
5719
5656
|
debugRect,
|
|
5657
|
+
debugPoly,
|
|
5720
5658
|
debugCircle,
|
|
5721
5659
|
debugPoint,
|
|
5722
5660
|
debugLine,
|
|
5723
|
-
|
|
5661
|
+
debugOverlap,
|
|
5724
5662
|
debugText,
|
|
5725
5663
|
debugClear,
|
|
5726
5664
|
debugSaveCanvas,
|
|
5665
|
+
debugSaveText,
|
|
5666
|
+
debugSaveDataURL,
|
|
5727
5667
|
|
|
5728
5668
|
// Settings
|
|
5729
5669
|
cameraPos,
|
|
@@ -5875,7 +5815,6 @@ export {
|
|
|
5875
5815
|
glCompileShader,
|
|
5876
5816
|
glCreateProgram,
|
|
5877
5817
|
glCreateTexture,
|
|
5878
|
-
glInitPostProcess,
|
|
5879
5818
|
|
|
5880
5819
|
// Input
|
|
5881
5820
|
keyIsDown,
|
|
@@ -5934,7 +5873,5 @@ export {
|
|
|
5934
5873
|
medals,
|
|
5935
5874
|
medalsPreventUnlock,
|
|
5936
5875
|
medalsInit,
|
|
5937
|
-
newgroundsInit,
|
|
5938
5876
|
Medal,
|
|
5939
|
-
Newgrounds,
|
|
5940
5877
|
};
|