littlejsengine 1.8.9 → 1.9.1
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/README.md +17 -17
- package/build/littlejs.d.ts +352 -288
- package/build/littlejs.esm.js +695 -658
- package/build/littlejs.esm.min.js +1 -1
- package/build/littlejs.js +694 -658
- package/build/littlejs.min.js +1 -1
- package/build/littlejs.release.js +629 -594
- package/examples/breakout/game.js +4 -4
- package/examples/breakout/index.html +3 -3
- package/examples/breakoutTutorial/index.html +2 -2
- package/examples/electron/game.js +1 -1
- package/examples/electron/index.html +2 -2
- package/examples/favicon.png +0 -0
- package/examples/js13k/build.js +1 -1
- package/examples/js13k/index.html +13 -13
- package/examples/module/game.js +1 -1
- package/examples/module/index.html +1 -1
- package/examples/particles/index.html +1 -1
- package/examples/platformer/game.js +6 -6
- package/examples/platformer/gameCharacter.js +293 -0
- package/examples/platformer/gameEffects.js +8 -5
- package/examples/platformer/gameObjects.js +13 -13
- package/examples/platformer/gamePlayer.js +9 -293
- package/examples/platformer/index.html +7 -6
- package/examples/puzzle/game.js +3 -2
- package/examples/puzzle/index.html +2 -2
- package/examples/starter/build.js +1 -1
- package/examples/starter/game.js +2 -2
- package/examples/starter/index.html +13 -13
- package/examples/stress/index.html +35 -27
- package/examples/typescript/index.html +1 -1
- package/index.d.ts +2094 -0
- package/package.json +1 -1
- package/src/engine.js +28 -28
- package/src/engineAudio.js +57 -57
- package/src/engineDebug.js +66 -65
- package/src/engineDraw.js +64 -73
- package/src/engineExport.js +1 -0
- package/src/engineInput.js +57 -40
- package/src/engineMedals.js +32 -29
- package/src/engineObject.js +42 -27
- package/src/engineParticles.js +98 -72
- package/src/engineRelease.js +1 -1
- package/src/engineSettings.js +22 -22
- package/src/engineTileLayer.js +66 -60
- package/src/engineUtilities.js +49 -49
- package/src/engineWebGL.js +112 -135
- package/src/jsconfig.json +10 -0
package/build/littlejs.esm.js
CHANGED
|
@@ -18,13 +18,13 @@
|
|
|
18
18
|
* @type {Boolean}
|
|
19
19
|
* @default
|
|
20
20
|
* @memberof Debug */
|
|
21
|
-
const debug =
|
|
21
|
+
const debug = true;
|
|
22
22
|
|
|
23
23
|
/** True if asserts are enaled
|
|
24
24
|
* @type {Boolean}
|
|
25
25
|
* @default
|
|
26
26
|
* @memberof Debug */
|
|
27
|
-
const enableAsserts =
|
|
27
|
+
const enableAsserts = true;
|
|
28
28
|
|
|
29
29
|
/** Size to render debug points by default
|
|
30
30
|
* @type {Number}
|
|
@@ -36,82 +36,87 @@ const debugPointSize = .5;
|
|
|
36
36
|
* @type {Boolean}
|
|
37
37
|
* @default
|
|
38
38
|
* @memberof Debug */
|
|
39
|
-
let showWatermark =
|
|
39
|
+
let showWatermark = true;
|
|
40
40
|
|
|
41
41
|
/** Key code used to toggle debug mode, Esc by default
|
|
42
|
+
* @type {String}
|
|
43
|
+
* @default
|
|
44
|
+
* @memberof Debug */
|
|
45
|
+
let debugKey = 'Escape';
|
|
46
|
+
|
|
47
|
+
/** True if the debug overlay is active, always false in release builds
|
|
42
48
|
* @type {Boolean}
|
|
43
49
|
* @default
|
|
44
50
|
* @memberof Debug */
|
|
45
|
-
let
|
|
51
|
+
let debugOverlay = false;
|
|
46
52
|
|
|
47
53
|
// Engine internal variables not exposed to documentation
|
|
48
|
-
let debugPrimitives = [],
|
|
49
|
-
debugParticles = 0, debugGamepads = 0, debugMedals = 0, debugTakeScreenshot, downloadLink;
|
|
54
|
+
let debugPrimitives = [], debugPhysics = false, debugRaycast = false, debugParticles = false, debugGamepads = false, debugMedals = false, debugTakeScreenshot, downloadLink;
|
|
50
55
|
|
|
51
56
|
///////////////////////////////////////////////////////////////////////////////
|
|
52
57
|
// Debug helper functions
|
|
53
58
|
|
|
54
59
|
/** Asserts if the experssion is false, does not do anything in release builds
|
|
55
|
-
* @param {Boolean}
|
|
56
|
-
* @param {Object}
|
|
60
|
+
* @param {Boolean} assert
|
|
61
|
+
* @param {Object} output
|
|
57
62
|
* @memberof Debug */
|
|
58
|
-
function ASSERT(
|
|
63
|
+
function ASSERT(assert, output) { enableAsserts && console.assert(assert, output); }
|
|
59
64
|
|
|
60
65
|
/** Draw a debug rectangle in world space
|
|
61
66
|
* @param {Vector2} pos
|
|
62
67
|
* @param {Vector2} [size=Vector2()]
|
|
63
|
-
* @param {String} [color
|
|
64
|
-
* @param {Number} [time
|
|
65
|
-
* @param {Number} [angle
|
|
66
|
-
* @param {Boolean} [fill
|
|
68
|
+
* @param {String} [color]
|
|
69
|
+
* @param {Number} [time]
|
|
70
|
+
* @param {Number} [angle]
|
|
71
|
+
* @param {Boolean} [fill]
|
|
67
72
|
* @memberof Debug */
|
|
68
73
|
function debugRect(pos, size=vec2(), color='#fff', time=0, angle=0, fill=false)
|
|
69
74
|
{
|
|
70
|
-
ASSERT(typeof color == 'string'
|
|
75
|
+
ASSERT(typeof color == 'string', 'pass in css color strings');
|
|
71
76
|
debugPrimitives.push({pos, size:vec2(size), color, time:new Timer(time), angle, fill});
|
|
72
77
|
}
|
|
73
78
|
|
|
74
79
|
/** Draw a debug circle in world space
|
|
75
80
|
* @param {Vector2} pos
|
|
76
|
-
* @param {Number} [radius
|
|
77
|
-
* @param {String} [color
|
|
78
|
-
* @param {Number} [time
|
|
79
|
-
* @param {Boolean} [fill
|
|
81
|
+
* @param {Number} [radius]
|
|
82
|
+
* @param {String} [color]
|
|
83
|
+
* @param {Number} [time]
|
|
84
|
+
* @param {Boolean} [fill]
|
|
80
85
|
* @memberof Debug */
|
|
81
86
|
function debugCircle(pos, radius=0, color='#fff', time=0, fill=false)
|
|
82
87
|
{
|
|
83
|
-
ASSERT(typeof color == 'string'
|
|
88
|
+
ASSERT(typeof color == 'string', 'pass in css color strings');
|
|
84
89
|
debugPrimitives.push({pos, size:radius, color, time:new Timer(time), angle:0, fill});
|
|
85
90
|
}
|
|
86
91
|
|
|
87
92
|
/** Draw a debug point in world space
|
|
88
93
|
* @param {Vector2} pos
|
|
89
|
-
* @param {String} [color
|
|
90
|
-
* @param {Number} [time
|
|
91
|
-
* @param {Number} [angle
|
|
94
|
+
* @param {String} [color]
|
|
95
|
+
* @param {Number} [time]
|
|
96
|
+
* @param {Number} [angle]
|
|
92
97
|
* @memberof Debug */
|
|
93
|
-
function debugPoint(pos, color, time, angle) {debugRect(pos,
|
|
98
|
+
function debugPoint(pos, color, time, angle) {debugRect(pos, undefined, color, time, angle);}
|
|
94
99
|
|
|
95
100
|
/** Draw a debug line in world space
|
|
96
101
|
* @param {Vector2} posA
|
|
97
102
|
* @param {Vector2} posB
|
|
98
|
-
* @param {String} [color
|
|
99
|
-
* @param {Number} [thickness
|
|
100
|
-
* @param {Number} [time
|
|
103
|
+
* @param {String} [color]
|
|
104
|
+
* @param {Number} [thickness]
|
|
105
|
+
* @param {Number} [time]
|
|
101
106
|
* @memberof Debug */
|
|
102
107
|
function debugLine(posA, posB, color, thickness=.1, time)
|
|
103
108
|
{
|
|
104
109
|
const halfDelta = vec2((posB.x - posA.x)/2, (posB.y - posA.y)/2);
|
|
105
110
|
const size = vec2(thickness, halfDelta.length()*2);
|
|
106
|
-
debugRect(posA.add(halfDelta), size, color, time, halfDelta.angle(),
|
|
111
|
+
debugRect(posA.add(halfDelta), size, color, time, halfDelta.angle(), true);
|
|
107
112
|
}
|
|
108
113
|
|
|
109
114
|
/** Draw a debug axis aligned bounding box in world space
|
|
110
|
-
* @param {Vector2}
|
|
111
|
-
* @param {Vector2}
|
|
112
|
-
* @param {Vector2}
|
|
113
|
-
* @param {Vector2}
|
|
114
|
-
* @param {String} [color
|
|
115
|
+
* @param {Vector2} pA - position A
|
|
116
|
+
* @param {Vector2} sA - size A
|
|
117
|
+
* @param {Vector2} pB - position B
|
|
118
|
+
* @param {Vector2} sB - size B
|
|
119
|
+
* @param {String} [color]
|
|
115
120
|
* @memberof Debug */
|
|
116
121
|
function debugAABB(pA, sA, pB, sB, color)
|
|
117
122
|
{
|
|
@@ -123,15 +128,15 @@ function debugAABB(pA, sA, pB, sB, color)
|
|
|
123
128
|
/** Draw a debug axis aligned bounding box in world space
|
|
124
129
|
* @param {String} text
|
|
125
130
|
* @param {Vector2} pos
|
|
126
|
-
* @param {Number} [size
|
|
127
|
-
* @param {String} [color
|
|
128
|
-
* @param {Number} [time
|
|
129
|
-
* @param {Number} [angle
|
|
130
|
-
* @param {String} [font
|
|
131
|
+
* @param {Number} [size]
|
|
132
|
+
* @param {String} [color]
|
|
133
|
+
* @param {Number} [time]
|
|
134
|
+
* @param {Number} [angle]
|
|
135
|
+
* @param {String} [font]
|
|
131
136
|
* @memberof Debug */
|
|
132
137
|
function debugText(text, pos, size=1, color='#fff', time=0, angle=0, font='monospace')
|
|
133
138
|
{
|
|
134
|
-
ASSERT(typeof color == 'string'
|
|
139
|
+
ASSERT(typeof color == 'string', 'pass in css color strings');
|
|
135
140
|
debugPrimitives.push({text, pos, size, color, time:new Timer(time), angle, font});
|
|
136
141
|
}
|
|
137
142
|
|
|
@@ -142,7 +147,7 @@ function debugClear() { debugPrimitives = []; }
|
|
|
142
147
|
/** Save a canvas to disk
|
|
143
148
|
* @param {HTMLCanvasElement} canvas
|
|
144
149
|
* @param {String} [filename]
|
|
145
|
-
* @param {String} [type
|
|
150
|
+
* @param {String} [type]
|
|
146
151
|
* @memberof Debug */
|
|
147
152
|
function debugSaveCanvas(canvas, filename=engineName, type='image/png')
|
|
148
153
|
{ debugSaveDataURL(canvas.toDataURL(type), filename); }
|
|
@@ -150,7 +155,7 @@ function debugSaveCanvas(canvas, filename=engineName, type='image/png')
|
|
|
150
155
|
/** Save a text file to disk
|
|
151
156
|
* @param {String} text
|
|
152
157
|
* @param {String} [filename]
|
|
153
|
-
* @param {String} [type
|
|
158
|
+
* @param {String} [type]
|
|
154
159
|
* @memberof Debug */
|
|
155
160
|
function debugSaveText(text, filename=engineName, type='text/plain')
|
|
156
161
|
{ debugSaveDataURL(URL.createObjectURL(new Blob([text], {'type':type})), filename); }
|
|
@@ -185,22 +190,18 @@ function debugUpdate()
|
|
|
185
190
|
debugOverlay = !debugOverlay;
|
|
186
191
|
if (debugOverlay)
|
|
187
192
|
{
|
|
188
|
-
if (keyWasPressed(
|
|
193
|
+
if (keyWasPressed('Digit0'))
|
|
189
194
|
showWatermark = !showWatermark;
|
|
190
|
-
if (keyWasPressed(
|
|
191
|
-
debugPhysics = !debugPhysics, debugParticles =
|
|
192
|
-
if (keyWasPressed(
|
|
193
|
-
debugParticles = !debugParticles, debugPhysics =
|
|
194
|
-
if (keyWasPressed(
|
|
195
|
+
if (keyWasPressed('Digit1'))
|
|
196
|
+
debugPhysics = !debugPhysics, debugParticles = false;
|
|
197
|
+
if (keyWasPressed('Digit2'))
|
|
198
|
+
debugParticles = !debugParticles, debugPhysics = false;
|
|
199
|
+
if (keyWasPressed('Digit3'))
|
|
195
200
|
debugGamepads = !debugGamepads;
|
|
196
|
-
if (keyWasPressed(
|
|
201
|
+
if (keyWasPressed('Digit4'))
|
|
197
202
|
debugRaycast = !debugRaycast;
|
|
198
|
-
if (keyWasPressed(
|
|
203
|
+
if (keyWasPressed('Digit5'))
|
|
199
204
|
debugTakeScreenshot = 1;
|
|
200
|
-
//if (keyWasPressed(54)) // 6
|
|
201
|
-
//if (keyWasPressed(55)) // 7
|
|
202
|
-
//if (keyWasPressed(56)) // 8
|
|
203
|
-
//if (keyWasPressed(57)) // 9
|
|
204
205
|
}
|
|
205
206
|
}
|
|
206
207
|
|
|
@@ -211,7 +212,7 @@ function debugRender()
|
|
|
211
212
|
if (debugTakeScreenshot)
|
|
212
213
|
{
|
|
213
214
|
// composite canvas
|
|
214
|
-
glCopyToContext(mainContext,
|
|
215
|
+
glCopyToContext(mainContext, true);
|
|
215
216
|
mainContext.drawImage(overlayCanvas, 0, 0);
|
|
216
217
|
overlayCanvas.width |= 0;
|
|
217
218
|
|
|
@@ -240,7 +241,7 @@ function debugRender()
|
|
|
240
241
|
{
|
|
241
242
|
const drawPos = centerPos.add(vec2(j*stickScale*2, i*stickScale*3));
|
|
242
243
|
const stickPos = drawPos.add(sticks[j].scale(stickScale));
|
|
243
|
-
debugCircle(drawPos, stickScale, '#fff7',0,
|
|
244
|
+
debugCircle(drawPos, stickScale, '#fff7',0,true);
|
|
244
245
|
debugLine(drawPos, stickPos, '#f00');
|
|
245
246
|
debugPoint(stickPos, '#f00');
|
|
246
247
|
}
|
|
@@ -248,8 +249,8 @@ function debugRender()
|
|
|
248
249
|
{
|
|
249
250
|
const drawPos = centerPos.add(vec2(j*buttonScale*2, i*stickScale*3-stickScale-buttonScale));
|
|
250
251
|
const pressed = gamepad.buttons[j].pressed;
|
|
251
|
-
debugCircle(drawPos, buttonScale, pressed ? '#f00' : '#fff7', 0,
|
|
252
|
-
debugText(j, drawPos, .2);
|
|
252
|
+
debugCircle(drawPos, buttonScale, pressed ? '#f00' : '#fff7', 0, true);
|
|
253
|
+
debugText(''+j, drawPos, .2);
|
|
253
254
|
}
|
|
254
255
|
}
|
|
255
256
|
}
|
|
@@ -278,25 +279,25 @@ function debugRender()
|
|
|
278
279
|
|
|
279
280
|
// show object info
|
|
280
281
|
const size = vec2(max(o.size.x, .2), max(o.size.y, .2));
|
|
281
|
-
const color1 = new Color(
|
|
282
|
+
const color1 = new Color(o.collideTiles?1:0, o.collideSolidObjects?1:0, o.isSolid?1:0, o.parent?.2:.5);
|
|
282
283
|
const color2 = o.parent ? new Color(1,1,1,.5) : new Color(0,0,0,.8);
|
|
283
|
-
drawRect(o.pos, size, color1, o.angle,
|
|
284
|
-
drawRect(o.pos, size.scale(.8), color2, o.angle,
|
|
285
|
-
o.parent && drawLine(o.pos, o.parent.pos, .1, new Color(0,0,1,.5),
|
|
284
|
+
drawRect(o.pos, size, color1, o.angle, false);
|
|
285
|
+
drawRect(o.pos, size.scale(.8), color2, o.angle, false);
|
|
286
|
+
o.parent && drawLine(o.pos, o.parent.pos, .1, new Color(0,0,1,.5), false);
|
|
286
287
|
}
|
|
287
288
|
|
|
288
289
|
if (bestObject)
|
|
289
290
|
{
|
|
290
291
|
const raycastHitPos = tileCollisionRaycast(bestObject.pos, mousePos);
|
|
291
292
|
raycastHitPos && drawRect(raycastHitPos.floor().add(vec2(.5)), vec2(1), new Color(0,1,1,.3));
|
|
292
|
-
drawRect(mousePos.floor().add(vec2(.5)), vec2(1), new Color(0,0,1,.5), 0,
|
|
293
|
-
drawLine(mousePos, bestObject.pos, .1, raycastHitPos ? new Color(1,0,0,.5) : new Color(0,1,0,.5),
|
|
293
|
+
drawRect(mousePos.floor().add(vec2(.5)), vec2(1), new Color(0,0,1,.5), 0, false);
|
|
294
|
+
drawLine(mousePos, bestObject.pos, .1, raycastHitPos ? new Color(1,0,0,.5) : new Color(0,1,0,.5), false);
|
|
294
295
|
|
|
295
296
|
const debugText = 'mouse pos = ' + mousePos +
|
|
296
297
|
'\nmouse collision = ' + getTileCollisionData(mousePos) +
|
|
297
298
|
'\n\n--- object info ---\n' +
|
|
298
299
|
bestObject.toString();
|
|
299
|
-
drawTextScreen(debugText, mousePosScreen, 24, new Color, .05,
|
|
300
|
+
drawTextScreen(debugText, mousePosScreen, 24, new Color, .05, undefined, 'center', 'monospace');
|
|
300
301
|
}
|
|
301
302
|
|
|
302
303
|
glCopyToContext(mainContext = saveContext);
|
|
@@ -385,7 +386,7 @@ function debugRender()
|
|
|
385
386
|
let keysPressed = '';
|
|
386
387
|
for(const i in inputData[0])
|
|
387
388
|
{
|
|
388
|
-
if (
|
|
389
|
+
if (keyIsDown(i, 0))
|
|
389
390
|
keysPressed += i + ' ' ;
|
|
390
391
|
}
|
|
391
392
|
keysPressed && overlayContext.fillText('Keys Down: ' + keysPressed, x, y += h);
|
|
@@ -394,7 +395,7 @@ function debugRender()
|
|
|
394
395
|
if (inputData[1])
|
|
395
396
|
for(const i in inputData[1])
|
|
396
397
|
{
|
|
397
|
-
if (
|
|
398
|
+
if (keyIsDown(i, 1))
|
|
398
399
|
buttonsPressed += i + ' ' ;
|
|
399
400
|
}
|
|
400
401
|
buttonsPressed && overlayContext.fillText('Gamepad: ' + buttonsPressed, x, y += h);
|
|
@@ -456,15 +457,15 @@ function sign(value) { return Math.sign(value); }
|
|
|
456
457
|
|
|
457
458
|
/** Returns first parm modulo the second param, but adjusted so negative numbers work as expected
|
|
458
459
|
* @param {Number} dividend
|
|
459
|
-
* @param {Number} [divisor
|
|
460
|
+
* @param {Number} [divisor]
|
|
460
461
|
* @return {Number}
|
|
461
462
|
* @memberof Utilities */
|
|
462
463
|
function mod(dividend, divisor=1) { return ((dividend % divisor) + divisor) % divisor; }
|
|
463
464
|
|
|
464
465
|
/** Clamps the value beween max and min
|
|
465
466
|
* @param {Number} value
|
|
466
|
-
* @param {Number} [min
|
|
467
|
-
* @param {Number} [max
|
|
467
|
+
* @param {Number} [min]
|
|
468
|
+
* @param {Number} [max]
|
|
468
469
|
* @return {Number}
|
|
469
470
|
* @memberof Utilities */
|
|
470
471
|
function clamp(value, min=0, max=1) { return value < min ? min : value > max ? max : value; }
|
|
@@ -489,7 +490,7 @@ function lerp(percent, valueA, valueB) { return valueA + clamp(percent) * (value
|
|
|
489
490
|
/** Returns signed wrapped distance between the two values passed in
|
|
490
491
|
* @param {Number} valueA
|
|
491
492
|
* @param {Number} valueB
|
|
492
|
-
* @param {Number} [wrapSize
|
|
493
|
+
* @param {Number} [wrapSize]
|
|
493
494
|
* @returns {Number}
|
|
494
495
|
* @memberof Utilities */
|
|
495
496
|
function distanceWrap(valueA, valueB, wrapSize=1)
|
|
@@ -499,7 +500,7 @@ function distanceWrap(valueA, valueB, wrapSize=1)
|
|
|
499
500
|
* @param {Number} percent
|
|
500
501
|
* @param {Number} valueA
|
|
501
502
|
* @param {Number} valueB
|
|
502
|
-
* @param {Number} [wrapSize
|
|
503
|
+
* @param {Number} [wrapSize]
|
|
503
504
|
* @returns {Number}
|
|
504
505
|
* @memberof Utilities */
|
|
505
506
|
function lerpWrap(percent, valueA, valueB, wrapSize=1)
|
|
@@ -510,7 +511,7 @@ function lerpWrap(percent, valueA, valueB, wrapSize=1)
|
|
|
510
511
|
* @param {Number} angleB
|
|
511
512
|
* @returns {Number}
|
|
512
513
|
* @memberof Utilities */
|
|
513
|
-
function distanceAngle(angleA, angleB) { distanceWrap(angleA, angleB, 2*PI); }
|
|
514
|
+
function distanceAngle(angleA, angleB) { return distanceWrap(angleA, angleB, 2*PI); }
|
|
514
515
|
|
|
515
516
|
/** Linearly interpolates between the angles passed in with wrappping
|
|
516
517
|
* @param {Number} percent
|
|
@@ -533,11 +534,11 @@ function smoothStep(percent) { return percent * percent * (3 - 2 * percent); }
|
|
|
533
534
|
function nearestPowerOfTwo(value) { return 2**Math.ceil(Math.log2(value)); }
|
|
534
535
|
|
|
535
536
|
/** Returns true if two axis aligned bounding boxes are overlapping
|
|
536
|
-
* @param {Vector2} pointA
|
|
537
|
-
* @param {Vector2} sizeA
|
|
538
|
-
* @param {Vector2} pointB
|
|
539
|
-
* @param {Vector2} sizeB
|
|
540
|
-
* @return {Boolean}
|
|
537
|
+
* @param {Vector2} pointA - Center of box A
|
|
538
|
+
* @param {Vector2} sizeA - Size of box A
|
|
539
|
+
* @param {Vector2} pointB - Center of box B
|
|
540
|
+
* @param {Vector2} sizeB - Size of box B
|
|
541
|
+
* @return {Boolean} - True if overlapping
|
|
541
542
|
* @memberof Utilities */
|
|
542
543
|
function isOverlapping(pointA, sizeA, pointB, sizeB)
|
|
543
544
|
{
|
|
@@ -546,10 +547,10 @@ function isOverlapping(pointA, sizeA, pointB, sizeB)
|
|
|
546
547
|
}
|
|
547
548
|
|
|
548
549
|
/** Returns an oscillating wave between 0 and amplitude with frequency of 1 Hz by default
|
|
549
|
-
* @param {Number} [frequency
|
|
550
|
-
* @param {Number} [amplitude
|
|
551
|
-
* @param {Number} [t=time]
|
|
552
|
-
* @return {Number}
|
|
550
|
+
* @param {Number} [frequency] - Frequency of the wave in Hz
|
|
551
|
+
* @param {Number} [amplitude] - Amplitude (max height) of the wave
|
|
552
|
+
* @param {Number} [t=time] - Value to use for time of the wave
|
|
553
|
+
* @return {Number} - Value waving between 0 and amplitude
|
|
553
554
|
* @memberof Utilities */
|
|
554
555
|
function wave(frequency=1, amplitude=1, t=time)
|
|
555
556
|
{ return amplitude/2 * (1 - Math.cos(t*frequency*2*PI)); }
|
|
@@ -566,15 +567,15 @@ function formatTime(t) { return (t/60|0) + ':' + (t%60<10?'0':'') + (t%60|0); }
|
|
|
566
567
|
* @namespace Random */
|
|
567
568
|
|
|
568
569
|
/** Returns a random value between the two values passed in
|
|
569
|
-
* @param {Number} [valueA
|
|
570
|
-
* @param {Number} [valueB
|
|
570
|
+
* @param {Number} [valueA]
|
|
571
|
+
* @param {Number} [valueB]
|
|
571
572
|
* @return {Number}
|
|
572
573
|
* @memberof Random */
|
|
573
574
|
function rand(valueA=1, valueB=0) { return valueB + Math.random() * (valueA-valueB); }
|
|
574
575
|
|
|
575
576
|
/** Returns a floored random value the two values passed in
|
|
576
577
|
* @param {Number} valueA
|
|
577
|
-
* @param {Number} [valueB
|
|
578
|
+
* @param {Number} [valueB]
|
|
578
579
|
* @return {Number}
|
|
579
580
|
* @memberof Random */
|
|
580
581
|
function randInt(valueA, valueB=0) { return Math.floor(rand(valueA,valueB)); }
|
|
@@ -585,14 +586,14 @@ function randInt(valueA, valueB=0) { return Math.floor(rand(valueA,valueB)); }
|
|
|
585
586
|
function randSign() { return randInt(2) * 2 - 1; }
|
|
586
587
|
|
|
587
588
|
/** Returns a random Vector2 with the passed in length
|
|
588
|
-
* @param {Number} [length
|
|
589
|
+
* @param {Number} [length]
|
|
589
590
|
* @return {Vector2}
|
|
590
591
|
* @memberof Random */
|
|
591
592
|
function randVector(length=1) { return new Vector2().setAngle(rand(2*PI), length); }
|
|
592
593
|
|
|
593
594
|
/** Returns a random Vector2 within a circular shape
|
|
594
|
-
* @param {Number} [radius
|
|
595
|
-
* @param {Number} [minRadius
|
|
595
|
+
* @param {Number} [radius]
|
|
596
|
+
* @param {Number} [minRadius]
|
|
596
597
|
* @return {Vector2}
|
|
597
598
|
* @memberof Random */
|
|
598
599
|
function randInCircle(radius=1, minRadius=0)
|
|
@@ -604,7 +605,7 @@ function randInCircle(radius=1, minRadius=0)
|
|
|
604
605
|
* @param {Boolean} [linear]
|
|
605
606
|
* @return {Color}
|
|
606
607
|
* @memberof Random */
|
|
607
|
-
function randColor(colorA=new Color, colorB=new Color(0,0,0,1), linear)
|
|
608
|
+
function randColor(colorA=new Color, colorB=new Color(0,0,0,1), linear=false)
|
|
608
609
|
{
|
|
609
610
|
return linear ? colorA.lerp(colorB, rand()) :
|
|
610
611
|
new Color(rand(colorA.r,colorB.r), rand(colorA.g,colorB.g), rand(colorA.b,colorB.b), rand(colorA.a,colorB.a));
|
|
@@ -633,8 +634,8 @@ class RandomGenerator
|
|
|
633
634
|
}
|
|
634
635
|
|
|
635
636
|
/** Returns a seeded random value between the two values passed in
|
|
636
|
-
* @param {Number} [valueA
|
|
637
|
-
* @param {Number} [valueB
|
|
637
|
+
* @param {Number} [valueA]
|
|
638
|
+
* @param {Number} [valueB]
|
|
638
639
|
* @return {Number} */
|
|
639
640
|
float(valueA=1, valueB=0)
|
|
640
641
|
{
|
|
@@ -647,7 +648,7 @@ class RandomGenerator
|
|
|
647
648
|
|
|
648
649
|
/** Returns a floored seeded random value the two values passed in
|
|
649
650
|
* @param {Number} valueA
|
|
650
|
-
* @param {Number} [valueB
|
|
651
|
+
* @param {Number} [valueB]
|
|
651
652
|
* @return {Number} */
|
|
652
653
|
int(valueA, valueB=0) { return Math.floor(this.float(valueA, valueB)); }
|
|
653
654
|
|
|
@@ -660,8 +661,8 @@ class RandomGenerator
|
|
|
660
661
|
|
|
661
662
|
/**
|
|
662
663
|
* Create a 2d vector, can take another Vector2 to copy, 2 scalars, or 1 scalar
|
|
663
|
-
* @param {(Number|Vector2)} [x
|
|
664
|
-
* @param {Number} [y
|
|
664
|
+
* @param {(Number|Vector2)} [x]
|
|
665
|
+
* @param {Number} [y]
|
|
665
666
|
* @return {Vector2}
|
|
666
667
|
* @example
|
|
667
668
|
* let a = vec2(0, 1); // vector with coordinates (0, 1)
|
|
@@ -671,15 +672,15 @@ class RandomGenerator
|
|
|
671
672
|
* @memberof Utilities
|
|
672
673
|
*/
|
|
673
674
|
function vec2(x=0, y)
|
|
674
|
-
{ return x
|
|
675
|
+
{ return typeof x === 'number'? new Vector2(x, y == undefined? x : y) : new Vector2(x.x, x.y); }
|
|
675
676
|
|
|
676
677
|
/**
|
|
677
678
|
* Check if object is a valid Vector2
|
|
678
|
-
* @param {
|
|
679
|
+
* @param {any} v
|
|
679
680
|
* @return {Boolean}
|
|
680
681
|
* @memberof Utilities
|
|
681
682
|
*/
|
|
682
|
-
function isVector2(v) { return
|
|
683
|
+
function isVector2(v) { return typeof v === 'object' && typeof v.x === 'number' && typeof v.y === 'number'; }
|
|
683
684
|
|
|
684
685
|
/**
|
|
685
686
|
* 2D Vector object with vector math library
|
|
@@ -693,8 +694,8 @@ function isVector2(v) { return !isNaN(v.x) && !isNaN(v.y); }
|
|
|
693
694
|
class Vector2
|
|
694
695
|
{
|
|
695
696
|
/** Create a 2D vector with the x and y passed in, can also be created with vec2()
|
|
696
|
-
* @param {Number} [x
|
|
697
|
-
* @param {Number} [y
|
|
697
|
+
* @param {Number} [x] - X axis location
|
|
698
|
+
* @param {Number} [y] - Y axis location */
|
|
698
699
|
constructor(x=0, y=0)
|
|
699
700
|
{
|
|
700
701
|
/** @property {Number} - X axis location */
|
|
@@ -751,12 +752,12 @@ class Vector2
|
|
|
751
752
|
distanceSquared(v) { return (this.x - v.x)**2 + (this.y - v.y)**2; }
|
|
752
753
|
|
|
753
754
|
/** Returns a new vector in same direction as this one with the length passed in
|
|
754
|
-
* @param {Number} [length
|
|
755
|
+
* @param {Number} [length]
|
|
755
756
|
* @return {Vector2} */
|
|
756
757
|
normalize(length=1) { const l = this.length(); return l ? this.scale(length/l) : new Vector2(0, length); }
|
|
757
758
|
|
|
758
759
|
/** Returns a new vector clamped to length passed in
|
|
759
|
-
* @param {Number} [length
|
|
760
|
+
* @param {Number} [length]
|
|
760
761
|
* @return {Vector2} */
|
|
761
762
|
clampLength(length=1) { const l = this.length(); return l > length ? this.scale(length/l) : this; }
|
|
762
763
|
|
|
@@ -775,8 +776,8 @@ class Vector2
|
|
|
775
776
|
angle() { return Math.atan2(this.x, this.y); }
|
|
776
777
|
|
|
777
778
|
/** Sets this vector with angle and length passed in
|
|
778
|
-
* @param {Number} [angle
|
|
779
|
-
* @param {Number} [length
|
|
779
|
+
* @param {Number} [angle]
|
|
780
|
+
* @param {Number} [length]
|
|
780
781
|
* @return {Vector2} */
|
|
781
782
|
setAngle(angle=0, length=1)
|
|
782
783
|
{ this.x = length*Math.sin(angle); this.y = length*Math.cos(angle); return this; }
|
|
@@ -861,10 +862,10 @@ function hsl(h, s, l, a) { return new Color().setHSLA(h, s, l, a); }
|
|
|
861
862
|
class Color
|
|
862
863
|
{
|
|
863
864
|
/** Create a color with the rgba components passed in, white by default
|
|
864
|
-
* @param {Number} [r
|
|
865
|
-
* @param {Number} [g
|
|
866
|
-
* @param {Number} [b
|
|
867
|
-
* @param {Number} [a
|
|
865
|
+
* @param {Number} [r] - red
|
|
866
|
+
* @param {Number} [g] - green
|
|
867
|
+
* @param {Number} [b] - blue
|
|
868
|
+
* @param {Number} [a] - alpha*/
|
|
868
869
|
constructor(r=1, g=1, b=1, a=1)
|
|
869
870
|
{
|
|
870
871
|
/** @property {Number} - Red */
|
|
@@ -919,10 +920,10 @@ class Color
|
|
|
919
920
|
lerp(c, percent) { return this.add(c.subtract(this).scale(clamp(percent))); }
|
|
920
921
|
|
|
921
922
|
/** Sets this color given a hue, saturation, lightness, and alpha
|
|
922
|
-
* @param {Number} [h
|
|
923
|
-
* @param {Number} [s
|
|
924
|
-
* @param {Number} [l
|
|
925
|
-
* @param {Number} [a
|
|
923
|
+
* @param {Number} [h] - hue
|
|
924
|
+
* @param {Number} [s] - saturation
|
|
925
|
+
* @param {Number} [l] - lightness
|
|
926
|
+
* @param {Number} [a] - alpha
|
|
926
927
|
* @return {Color} */
|
|
927
928
|
setHSLA(h=0, s=0, l=1, a=1)
|
|
928
929
|
{
|
|
@@ -968,8 +969,8 @@ class Color
|
|
|
968
969
|
}
|
|
969
970
|
|
|
970
971
|
/** Returns a new color that has each component randomly adjusted
|
|
971
|
-
* @param {Number} [amount
|
|
972
|
-
* @param {Number} [alphaAmount
|
|
972
|
+
* @param {Number} [amount]
|
|
973
|
+
* @param {Number} [alphaAmount]
|
|
973
974
|
* @return {Color} */
|
|
974
975
|
mutate(amount=.05, alphaAmount=0)
|
|
975
976
|
{
|
|
@@ -983,9 +984,9 @@ class Color
|
|
|
983
984
|
}
|
|
984
985
|
|
|
985
986
|
/** Returns this color expressed as a hex color code
|
|
986
|
-
* @param {Boolean} [useAlpha
|
|
987
|
+
* @param {Boolean} [useAlpha] - if alpha should be included in result
|
|
987
988
|
* @return {String} */
|
|
988
|
-
toString(useAlpha =
|
|
989
|
+
toString(useAlpha = true)
|
|
989
990
|
{
|
|
990
991
|
const toHex = (c)=> ((c=c*255|0)<16 ? '0' : '') + c.toString(16);
|
|
991
992
|
return '#' + toHex(this.r) + toHex(this.g) + toHex(this.b) + (useAlpha ? toHex(this.a) : '');
|
|
@@ -1034,7 +1035,7 @@ class Timer
|
|
|
1034
1035
|
constructor(timeLeft) { this.time = timeLeft == undefined ? undefined : time + timeLeft; this.setTime = timeLeft; }
|
|
1035
1036
|
|
|
1036
1037
|
/** Set the timer with seconds passed in
|
|
1037
|
-
* @param {Number} [timeLeft
|
|
1038
|
+
* @param {Number} [timeLeft] - How much time left before the timer is elapsed in seconds */
|
|
1038
1039
|
set(timeLeft=0) { this.time = time + timeLeft; this.setTime = timeLeft; }
|
|
1039
1040
|
|
|
1040
1041
|
/** Unset the timer */
|
|
@@ -1111,7 +1112,7 @@ let canvasFixedSize = vec2();
|
|
|
1111
1112
|
* @type {Boolean}
|
|
1112
1113
|
* @default
|
|
1113
1114
|
* @memberof Settings */
|
|
1114
|
-
let canvasPixelated =
|
|
1115
|
+
let canvasPixelated = true;
|
|
1115
1116
|
|
|
1116
1117
|
/** Default font used for text rendering
|
|
1117
1118
|
* @type {String}
|
|
@@ -1123,7 +1124,7 @@ let fontDefault = 'arial';
|
|
|
1123
1124
|
* @type {Boolean}
|
|
1124
1125
|
* @default
|
|
1125
1126
|
* @memberof Settings */
|
|
1126
|
-
let showSplashScreen =
|
|
1127
|
+
let showSplashScreen = false;
|
|
1127
1128
|
|
|
1128
1129
|
///////////////////////////////////////////////////////////////////////////////
|
|
1129
1130
|
// WebGL settings
|
|
@@ -1132,13 +1133,13 @@ let showSplashScreen = 0;
|
|
|
1132
1133
|
* @type {Boolean}
|
|
1133
1134
|
* @default
|
|
1134
1135
|
* @memberof Settings */
|
|
1135
|
-
let glEnable =
|
|
1136
|
+
let glEnable = true;
|
|
1136
1137
|
|
|
1137
1138
|
/** Fixes slow rendering in some browsers by not compositing the WebGL canvas
|
|
1138
1139
|
* @type {Boolean}
|
|
1139
1140
|
* @default
|
|
1140
1141
|
* @memberof Settings */
|
|
1141
|
-
let glOverlay =
|
|
1142
|
+
let glOverlay = true;
|
|
1142
1143
|
|
|
1143
1144
|
///////////////////////////////////////////////////////////////////////////////
|
|
1144
1145
|
// Tile sheet settings
|
|
@@ -1162,7 +1163,7 @@ let tileFixBleedScale = .3;
|
|
|
1162
1163
|
* @type {Boolean}
|
|
1163
1164
|
* @default
|
|
1164
1165
|
* @memberof Settings */
|
|
1165
|
-
let enablePhysicsSolver =
|
|
1166
|
+
let enablePhysicsSolver = true;
|
|
1166
1167
|
|
|
1167
1168
|
/** Default object mass for collison calcuations (how heavy objects are)
|
|
1168
1169
|
* @type {Number}
|
|
@@ -1184,7 +1185,7 @@ let objectDefaultAngleDamping = 1;
|
|
|
1184
1185
|
|
|
1185
1186
|
/** How much to bounce when a collision occurs (0-1)
|
|
1186
1187
|
* @type {Number}
|
|
1187
|
-
* @default
|
|
1188
|
+
* @default
|
|
1188
1189
|
* @memberof Settings */
|
|
1189
1190
|
let objectDefaultElasticity = 0;
|
|
1190
1191
|
|
|
@@ -1202,7 +1203,7 @@ let objectMaxSpeed = 1;
|
|
|
1202
1203
|
|
|
1203
1204
|
/** How much gravity to apply to objects along the Y axis, negative is down
|
|
1204
1205
|
* @type {Number}
|
|
1205
|
-
* @default
|
|
1206
|
+
* @default
|
|
1206
1207
|
* @memberof Settings */
|
|
1207
1208
|
let gravity = 0;
|
|
1208
1209
|
|
|
@@ -1219,33 +1220,33 @@ let particleEmitRateScale = 1;
|
|
|
1219
1220
|
* @type {Boolean}
|
|
1220
1221
|
* @default
|
|
1221
1222
|
* @memberof Settings */
|
|
1222
|
-
let gamepadsEnable =
|
|
1223
|
+
let gamepadsEnable = true;
|
|
1223
1224
|
|
|
1224
1225
|
/** If true, the dpad input is also routed to the left analog stick (for better accessability)
|
|
1225
1226
|
* @type {Boolean}
|
|
1226
1227
|
* @default
|
|
1227
1228
|
* @memberof Settings */
|
|
1228
|
-
let gamepadDirectionEmulateStick =
|
|
1229
|
+
let gamepadDirectionEmulateStick = true;
|
|
1229
1230
|
|
|
1230
1231
|
/** If true the WASD keys are also routed to the direction keys (for better accessability)
|
|
1231
1232
|
* @type {Boolean}
|
|
1232
1233
|
* @default
|
|
1233
1234
|
* @memberof Settings */
|
|
1234
|
-
let inputWASDEmulateDirection =
|
|
1235
|
+
let inputWASDEmulateDirection = true;
|
|
1235
1236
|
|
|
1236
1237
|
/** True if touch gamepad should appear on mobile devices
|
|
1237
1238
|
* - Supports left analog stick, 4 face buttons and start button (button 9)
|
|
1238
1239
|
* - Must be set by end of gameInit to be activated
|
|
1239
1240
|
* @type {Boolean}
|
|
1240
|
-
* @default
|
|
1241
|
+
* @default
|
|
1241
1242
|
* @memberof Settings */
|
|
1242
|
-
let touchGamepadEnable =
|
|
1243
|
+
let touchGamepadEnable = false;
|
|
1243
1244
|
|
|
1244
1245
|
/** True if touch gamepad should be analog stick or false to use if 8 way dpad
|
|
1245
1246
|
* @type {Boolean}
|
|
1246
1247
|
* @default
|
|
1247
1248
|
* @memberof Settings */
|
|
1248
|
-
let touchGamepadAnalog =
|
|
1249
|
+
let touchGamepadAnalog = true;
|
|
1249
1250
|
|
|
1250
1251
|
/** Size of virutal gamepad for touch devices in pixels
|
|
1251
1252
|
* @type {Number}
|
|
@@ -1263,7 +1264,7 @@ let touchGamepadAlpha = .3;
|
|
|
1263
1264
|
* @type {Boolean}
|
|
1264
1265
|
* @default
|
|
1265
1266
|
* @memberof Settings */
|
|
1266
|
-
let vibrateEnable =
|
|
1267
|
+
let vibrateEnable = true;
|
|
1267
1268
|
|
|
1268
1269
|
///////////////////////////////////////////////////////////////////////////////
|
|
1269
1270
|
// Audio settings
|
|
@@ -1272,7 +1273,7 @@ let vibrateEnable = 1;
|
|
|
1272
1273
|
* @type {Boolean}
|
|
1273
1274
|
* @default
|
|
1274
1275
|
* @memberof Settings */
|
|
1275
|
-
let soundEnable =
|
|
1276
|
+
let soundEnable = true;
|
|
1276
1277
|
|
|
1277
1278
|
/** Volume scale to apply to all sound, music and speech
|
|
1278
1279
|
* @type {Number}
|
|
@@ -1321,9 +1322,9 @@ let medalDisplayIconSize = 50;
|
|
|
1321
1322
|
|
|
1322
1323
|
/** Set to stop medals from being unlockable (like if cheats are enabled)
|
|
1323
1324
|
* @type {Boolean}
|
|
1324
|
-
* @default
|
|
1325
|
+
* @default
|
|
1325
1326
|
* @memberof Settings */
|
|
1326
|
-
let medalsPreventUnlock;
|
|
1327
|
+
let medalsPreventUnlock = false;
|
|
1327
1328
|
|
|
1328
1329
|
///////////////////////////////////////////////////////////////////////////////
|
|
1329
1330
|
// Setters for global variables
|
|
@@ -1394,12 +1395,12 @@ function setEnablePhysicsSolver(enable) { enablePhysicsSolver = enable; }
|
|
|
1394
1395
|
function setObjectDefaultMass(mass) { objectDefaultMass = mass; }
|
|
1395
1396
|
|
|
1396
1397
|
/** Set how much to slow velocity by each frame
|
|
1397
|
-
* @param {Number}
|
|
1398
|
+
* @param {Number} damp
|
|
1398
1399
|
* @memberof Settings */
|
|
1399
1400
|
function setObjectDefaultDamping(damp) { objectDefaultDamping = damp; }
|
|
1400
1401
|
|
|
1401
1402
|
/** Set how much to slow angular velocity each frame
|
|
1402
|
-
* @param {Number}
|
|
1403
|
+
* @param {Number} damp
|
|
1403
1404
|
* @memberof Settings */
|
|
1404
1405
|
function setObjectDefaultAngleDamping(damp) { objectDefaultAngleDamping = damp; }
|
|
1405
1406
|
|
|
@@ -1419,9 +1420,9 @@ function setObjectDefaultFriction(friction) { objectDefaultFriction = friction;
|
|
|
1419
1420
|
function setObjectMaxSpeed(speed) { objectMaxSpeed = speed; }
|
|
1420
1421
|
|
|
1421
1422
|
/** Set how much gravity to apply to objects along the Y axis
|
|
1422
|
-
* @param {Number}
|
|
1423
|
+
* @param {Number} newGravity
|
|
1423
1424
|
* @memberof Settings */
|
|
1424
|
-
function setGravity(
|
|
1425
|
+
function setGravity(newGravity) { gravity = newGravity; }
|
|
1425
1426
|
|
|
1426
1427
|
/** Set to scales emit rate of particles
|
|
1427
1428
|
* @param {Number} scale
|
|
@@ -1519,7 +1520,7 @@ function setMedalsPreventUnlock(preventUnlock) { medalsPreventUnlock = preventUn
|
|
|
1519
1520
|
function setShowWatermark(show) { showWatermark = show; }
|
|
1520
1521
|
|
|
1521
1522
|
/** Set key code used to toggle debug mode, Esc by default
|
|
1522
|
-
* @param {
|
|
1523
|
+
* @param {String} key
|
|
1523
1524
|
* @memberof Debug */
|
|
1524
1525
|
function setDebugKey(key) { debugKey = key; }
|
|
1525
1526
|
/**
|
|
@@ -1556,27 +1557,25 @@ function setDebugKey(key) { debugKey = key; }
|
|
|
1556
1557
|
class EngineObject
|
|
1557
1558
|
{
|
|
1558
1559
|
/** Create an engine object and adds it to the list of objects
|
|
1559
|
-
* @param {Vector2}
|
|
1560
|
-
* @param {Vector2}
|
|
1561
|
-
* @param {TileInfo} [tileInfo]
|
|
1562
|
-
* @param {Number}
|
|
1563
|
-
* @param {Color}
|
|
1564
|
-
* @param {Number}
|
|
1560
|
+
* @param {Vector2} [pos=(0,0)] - World space position of the object
|
|
1561
|
+
* @param {Vector2} [size=(1,1)] - World space size of the object
|
|
1562
|
+
* @param {TileInfo} [tileInfo] - Tile info to render object (undefined is untextured)
|
|
1563
|
+
* @param {Number} [angle] - Angle the object is rotated by
|
|
1564
|
+
* @param {Color} [color=(1,1,1,1)] - Color to apply to tile when rendered
|
|
1565
|
+
* @param {Number} [renderOrder] - Objects sorted by renderOrder before being rendered
|
|
1565
1566
|
*/
|
|
1566
1567
|
constructor(pos=vec2(), size=vec2(1), tileInfo, angle=0, color, renderOrder=0)
|
|
1567
1568
|
{
|
|
1568
1569
|
// set passed in params
|
|
1569
|
-
ASSERT(isVector2(pos) && isVector2(size)
|
|
1570
|
-
ASSERT(typeof tileInfo !== 'number' || !tileInfo
|
|
1571
|
-
ASSERT(!(renderOrder instanceof Color)); // prevent old style calls
|
|
1572
|
-
// to fix old calls, replace with tile(tileIndex, tileSize)
|
|
1570
|
+
ASSERT(isVector2(pos) && isVector2(size), 'ensure pos and size are vec2s');
|
|
1571
|
+
ASSERT(typeof tileInfo !== 'number' || !tileInfo, 'old style tile setup');
|
|
1573
1572
|
|
|
1574
1573
|
/** @property {Vector2} - World space position of the object */
|
|
1575
1574
|
this.pos = pos.copy();
|
|
1576
1575
|
/** @property {Vector2} - World space width and height of the object */
|
|
1577
1576
|
this.size = size;
|
|
1578
1577
|
/** @property {Vector2} - Size of object used for drawing, uses size if not set */
|
|
1579
|
-
this.drawSize;
|
|
1578
|
+
this.drawSize = undefined;
|
|
1580
1579
|
/** @property {TileInfo} - Tile info to render object (undefined is untextured) */
|
|
1581
1580
|
this.tileInfo = tileInfo;
|
|
1582
1581
|
/** @property {Number} - Angle to rotate the object */
|
|
@@ -1584,9 +1583,11 @@ class EngineObject
|
|
|
1584
1583
|
/** @property {Color} - Color to apply when rendered */
|
|
1585
1584
|
this.color = color;
|
|
1586
1585
|
/** @property {Color} - Additive color to apply when rendered */
|
|
1587
|
-
this.additiveColor;
|
|
1586
|
+
this.additiveColor = undefined;
|
|
1587
|
+
/** @property {Boolean} - Should it flip along y axis when rendered */
|
|
1588
|
+
this.mirror = false;
|
|
1588
1589
|
|
|
1589
|
-
//
|
|
1590
|
+
// physical properties
|
|
1590
1591
|
/** @property {Number} [mass=objectDefaultMass] - How heavy the object is, static if 0 */
|
|
1591
1592
|
this.mass = objectDefaultMass;
|
|
1592
1593
|
/** @property {Number} [damping=objectDefaultDamping] - How much to slow down velocity each frame (0-1) */
|
|
@@ -1597,19 +1598,34 @@ class EngineObject
|
|
|
1597
1598
|
this.elasticity = objectDefaultElasticity;
|
|
1598
1599
|
/** @property {Number} [friction=objectDefaultFriction] - How much friction to apply when sliding (0-1) */
|
|
1599
1600
|
this.friction = objectDefaultFriction;
|
|
1600
|
-
/** @property {Number}
|
|
1601
|
+
/** @property {Number} - How much to scale gravity by for this object */
|
|
1601
1602
|
this.gravityScale = 1;
|
|
1602
|
-
/** @property {Number}
|
|
1603
|
+
/** @property {Number} - Objects are sorted by render order */
|
|
1603
1604
|
this.renderOrder = renderOrder;
|
|
1604
|
-
/** @property {Vector2}
|
|
1605
|
+
/** @property {Vector2} - Velocity of the object */
|
|
1605
1606
|
this.velocity = vec2();
|
|
1606
|
-
/** @property {Number}
|
|
1607
|
+
/** @property {Number} - Angular velocity of the object */
|
|
1607
1608
|
this.angleVelocity = 0;
|
|
1608
|
-
|
|
1609
|
-
// init other internal object stuff
|
|
1609
|
+
/** @property {Number} - Track when object was created */
|
|
1610
1610
|
this.spawnTime = time;
|
|
1611
|
+
/** @property {Array} - List of children of this object */
|
|
1611
1612
|
this.children = [];
|
|
1613
|
+
|
|
1614
|
+
// parent child system
|
|
1615
|
+
/** @property {EngineObject} - Parent of object if in local space */
|
|
1616
|
+
this.parent = undefined;
|
|
1617
|
+
/** @property {Vector2} - Local position if child */
|
|
1618
|
+
this.localPos = vec2();
|
|
1619
|
+
/** @property {Number} - Local angle if child */
|
|
1620
|
+
this.localAngle = 0;
|
|
1621
|
+
|
|
1622
|
+
// collision flags
|
|
1623
|
+
/** @property {Boolean} - Object collides with the tile collision */
|
|
1612
1624
|
this.collideTiles = false;
|
|
1625
|
+
/** @property {Boolean} - Object collides with solid objects */
|
|
1626
|
+
this.collideSolidObjects = false;
|
|
1627
|
+
/** @property {Boolean} - Object collides with and blocks other objects */
|
|
1628
|
+
this.isSolid = false;
|
|
1613
1629
|
|
|
1614
1630
|
// add to list of objects
|
|
1615
1631
|
engineObjects.push(this);
|
|
@@ -1826,7 +1842,7 @@ class EngineObject
|
|
|
1826
1842
|
* @param {EngineObject} object - the object to test against
|
|
1827
1843
|
* @return {Boolean} - true if the collision should be resolved
|
|
1828
1844
|
*/
|
|
1829
|
-
collideWithObject(object) { return
|
|
1845
|
+
collideWithObject(object) { return true; }
|
|
1830
1846
|
|
|
1831
1847
|
/** How long since the object was created
|
|
1832
1848
|
* @return {Number} */
|
|
@@ -1846,8 +1862,8 @@ class EngineObject
|
|
|
1846
1862
|
|
|
1847
1863
|
/** Attaches a child to this with a given local transform
|
|
1848
1864
|
* @param {EngineObject} child
|
|
1849
|
-
* @param {Vector2} [localPos=
|
|
1850
|
-
* @param {Number} [localAngle
|
|
1865
|
+
* @param {Vector2} [localPos=(0,0)]
|
|
1866
|
+
* @param {Number} [localAngle] */
|
|
1851
1867
|
addChild(child, localPos=vec2(), localAngle=0)
|
|
1852
1868
|
{
|
|
1853
1869
|
ASSERT(!child.parent && !this.children.includes(child));
|
|
@@ -1867,12 +1883,12 @@ class EngineObject
|
|
|
1867
1883
|
}
|
|
1868
1884
|
|
|
1869
1885
|
/** Set how this object collides
|
|
1870
|
-
* @param {Boolean} [collideSolidObjects
|
|
1871
|
-
* @param {Boolean} [isSolid
|
|
1872
|
-
* @param {Boolean} [collideTiles
|
|
1873
|
-
setCollision(collideSolidObjects=
|
|
1886
|
+
* @param {Boolean} [collideSolidObjects] - Does it collide with solid objects
|
|
1887
|
+
* @param {Boolean} [isSolid] - Does it collide with and block other objects (expensive in large numbers)
|
|
1888
|
+
* @param {Boolean} [collideTiles] - Does it collide with the tile collision */
|
|
1889
|
+
setCollision(collideSolidObjects=true, isSolid=true, collideTiles=true)
|
|
1874
1890
|
{
|
|
1875
|
-
ASSERT(collideSolidObjects || !isSolid
|
|
1891
|
+
ASSERT(collideSolidObjects || !isSolid, 'solid objects must be set to collide');
|
|
1876
1892
|
|
|
1877
1893
|
this.collideSolidObjects = collideSolidObjects;
|
|
1878
1894
|
this.isSolid = isSolid;
|
|
@@ -1963,9 +1979,9 @@ let drawCount;
|
|
|
1963
1979
|
* Create a tile info object
|
|
1964
1980
|
* - This can take vecs or floats for easier use and conversion
|
|
1965
1981
|
* - If an index is passed in, the tile size and index will determine the position
|
|
1966
|
-
* @param {(Number|Vector2)} [pos=
|
|
1967
|
-
* @param {(Number|Vector2)} [size=tileSizeDefault]
|
|
1968
|
-
* @param {Number} [textureIndex
|
|
1982
|
+
* @param {(Number|Vector2)} [pos=(0,0)] - Top left corner of tile in pixels or index
|
|
1983
|
+
* @param {(Number|Vector2)} [size=tileSizeDefault] - Size of tile in pixels
|
|
1984
|
+
* @param {Number} [textureIndex] - Texture index to use
|
|
1969
1985
|
* @return {TileInfo}
|
|
1970
1986
|
* @example
|
|
1971
1987
|
* tile(2) // a tile at index 2 using the default tile size of 16
|
|
@@ -1977,14 +1993,14 @@ let drawCount;
|
|
|
1977
1993
|
function tile(pos=vec2(), size=tileSizeDefault, textureIndex=0)
|
|
1978
1994
|
{
|
|
1979
1995
|
// if size is a number, make it a vector
|
|
1980
|
-
if (size
|
|
1996
|
+
if (typeof size === 'number')
|
|
1981
1997
|
{
|
|
1982
1998
|
ASSERT(size > 0);
|
|
1983
1999
|
size = vec2(size);
|
|
1984
2000
|
}
|
|
1985
2001
|
|
|
1986
2002
|
// if pos is a number, use it as a tile index
|
|
1987
|
-
if (pos
|
|
2003
|
+
if (typeof pos === 'number')
|
|
1988
2004
|
{
|
|
1989
2005
|
const textureInfo = textureInfos[textureIndex];
|
|
1990
2006
|
if (textureInfo)
|
|
@@ -2006,9 +2022,9 @@ function tile(pos=vec2(), size=tileSizeDefault, textureIndex=0)
|
|
|
2006
2022
|
class TileInfo
|
|
2007
2023
|
{
|
|
2008
2024
|
/** Create a tile info object
|
|
2009
|
-
* @param {Vector2} [pos=
|
|
2025
|
+
* @param {Vector2} [pos=(0,0)] - Top left corner of tile in pixels
|
|
2010
2026
|
* @param {Vector2} [size=tileSizeDefault] - Size of tile in pixels
|
|
2011
|
-
* @param {Number} [textureIndex
|
|
2027
|
+
* @param {Number} [textureIndex] - Texture index to use
|
|
2012
2028
|
*/
|
|
2013
2029
|
constructor(pos=vec2(), size=tileSizeDefault, textureIndex=0)
|
|
2014
2030
|
{
|
|
@@ -2037,10 +2053,13 @@ class TileInfo
|
|
|
2037
2053
|
/** Texture Info - Stores info about each texture */
|
|
2038
2054
|
class TextureInfo
|
|
2039
2055
|
{
|
|
2040
|
-
|
|
2056
|
+
/**
|
|
2057
|
+
* Create a TextureInfo, called automatically by the engine
|
|
2058
|
+
* @param {HTMLImageElement} image
|
|
2059
|
+
*/
|
|
2041
2060
|
constructor(image)
|
|
2042
2061
|
{
|
|
2043
|
-
/** @property {
|
|
2062
|
+
/** @property {HTMLImageElement} - image source */
|
|
2044
2063
|
this.image = image;
|
|
2045
2064
|
/** @property {Vector2} - size of the image */
|
|
2046
2065
|
this.size = vec2(image.width, image.height);
|
|
@@ -2080,26 +2099,24 @@ function worldToScreen(worldPos)
|
|
|
2080
2099
|
}
|
|
2081
2100
|
|
|
2082
2101
|
/** Draw textured tile centered in world space, with color applied if using WebGL
|
|
2083
|
-
* @param {Vector2} pos
|
|
2084
|
-
* @param {Vector2} [size=
|
|
2085
|
-
* @param {TileInfo}[tileInfo]
|
|
2086
|
-
* @param {
|
|
2087
|
-
* @param {
|
|
2088
|
-
* @param {
|
|
2089
|
-
* @param {
|
|
2090
|
-
* @param {
|
|
2091
|
-
* @param {Boolean} [
|
|
2092
|
-
* @param {
|
|
2093
|
-
* @param {CanvasRenderingContext2D} [context] - Canvas 2D context to draw to
|
|
2102
|
+
* @param {Vector2} pos - Center of the tile in world space
|
|
2103
|
+
* @param {Vector2} [size=(1,1)] - Size of the tile in world space
|
|
2104
|
+
* @param {TileInfo}[tileInfo] - Tile info to use, untextured if undefined
|
|
2105
|
+
* @param {Color} [color=(1,1,1,1)] - Color to modulate with
|
|
2106
|
+
* @param {Number} [angle] - Angle to rotate by
|
|
2107
|
+
* @param {Boolean} [mirror] - If true image is flipped along the Y axis
|
|
2108
|
+
* @param {Color} [additiveColor=(0,0,0,0)] - Additive color to be applied
|
|
2109
|
+
* @param {Boolean} [useWebGL=glEnable] - Use accelerated WebGL rendering
|
|
2110
|
+
* @param {Boolean} [screenSpace=false] - If true the pos and size are in screen space
|
|
2111
|
+
* @param {CanvasRenderingContext2D} [context] - Canvas 2D context to draw to
|
|
2094
2112
|
* @memberof Draw */
|
|
2095
2113
|
function drawTile(pos, size=vec2(1), tileInfo, color=new Color,
|
|
2096
2114
|
angle=0, mirror, additiveColor=new Color(0,0,0,0), useWebGL=glEnable, screenSpace, context)
|
|
2097
2115
|
{
|
|
2098
|
-
ASSERT(!context || !useWebGL
|
|
2099
|
-
ASSERT(typeof tileInfo !== 'number' || !tileInfo
|
|
2100
|
-
|
|
2116
|
+
ASSERT(!context || !useWebGL, 'context only supported in canvas 2D mode');
|
|
2117
|
+
ASSERT(typeof tileInfo !== 'number' || !tileInfo,
|
|
2118
|
+
'this is an old style calls, to fix replace it with tile(tileIndex, tileSize)');
|
|
2101
2119
|
|
|
2102
|
-
showWatermark && ++drawCount;
|
|
2103
2120
|
const textureInfo = tileInfo && tileInfo.getTextureInfo();
|
|
2104
2121
|
if (useWebGL)
|
|
2105
2122
|
{
|
|
@@ -2133,6 +2150,7 @@ function drawTile(pos, size=vec2(1), tileInfo, color=new Color,
|
|
|
2133
2150
|
else
|
|
2134
2151
|
{
|
|
2135
2152
|
// normal canvas 2D rendering method (slower)
|
|
2153
|
+
showWatermark && ++drawCount;
|
|
2136
2154
|
drawCanvas2D(pos, size, angle, mirror, (context)=>
|
|
2137
2155
|
{
|
|
2138
2156
|
if (textureInfo)
|
|
@@ -2158,51 +2176,40 @@ function drawTile(pos, size=vec2(1), tileInfo, color=new Color,
|
|
|
2158
2176
|
|
|
2159
2177
|
/** Draw colored rect centered on pos
|
|
2160
2178
|
* @param {Vector2} pos
|
|
2161
|
-
* @param {Vector2} [size=
|
|
2162
|
-
* @param {Color} [color=
|
|
2163
|
-
* @param {Number} [angle
|
|
2179
|
+
* @param {Vector2} [size=(1,1)]
|
|
2180
|
+
* @param {Color} [color=(1,1,1,1)]
|
|
2181
|
+
* @param {Number} [angle]
|
|
2164
2182
|
* @param {Boolean} [useWebGL=glEnable]
|
|
2165
|
-
* @param {Boolean} [screenSpace=
|
|
2183
|
+
* @param {Boolean} [screenSpace=false]
|
|
2166
2184
|
* @param {CanvasRenderingContext2D} [context]
|
|
2167
2185
|
* @memberof Draw */
|
|
2168
2186
|
function drawRect(pos, size, color, angle, useWebGL, screenSpace, context)
|
|
2169
2187
|
{
|
|
2170
|
-
drawTile(pos, size, undefined, color, angle,
|
|
2188
|
+
drawTile(pos, size, undefined, color, angle, false, undefined, useWebGL, screenSpace, context);
|
|
2171
2189
|
}
|
|
2172
2190
|
|
|
2173
2191
|
/** Draw colored polygon using passed in points
|
|
2174
2192
|
* @param {Array} points - Array of Vector2 points
|
|
2175
|
-
* @param {Color} [color=
|
|
2176
|
-
* @param {Boolean} [
|
|
2177
|
-
* @param {
|
|
2178
|
-
* @param {CanvasRenderingContext2D} [context]
|
|
2193
|
+
* @param {Color} [color=(1,1,1,1)]
|
|
2194
|
+
* @param {Boolean} [screenSpace=false]
|
|
2195
|
+
* @param {CanvasRenderingContext2D} [context=mainContext]
|
|
2179
2196
|
* @memberof Draw */
|
|
2180
|
-
function drawPoly(points, color=new Color,
|
|
2197
|
+
function drawPoly(points, color=new Color, screenSpace, context=mainContext)
|
|
2181
2198
|
{
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
{
|
|
2188
|
-
// draw using canvas
|
|
2189
|
-
if (!context)
|
|
2190
|
-
context = mainContext;
|
|
2191
|
-
context.fillStyle = color;
|
|
2192
|
-
context.beginPath();
|
|
2193
|
-
for (const point of screenSpace ? points : points.map(worldToScreen))
|
|
2194
|
-
context.lineTo(point.x, point.y);
|
|
2195
|
-
context.fill();
|
|
2196
|
-
}
|
|
2199
|
+
context.fillStyle = color.toString();
|
|
2200
|
+
context.beginPath();
|
|
2201
|
+
for (const point of screenSpace ? points : points.map(worldToScreen))
|
|
2202
|
+
context.lineTo(point.x, point.y);
|
|
2203
|
+
context.fill();
|
|
2197
2204
|
}
|
|
2198
2205
|
|
|
2199
2206
|
/** Draw colored line between two points
|
|
2200
2207
|
* @param {Vector2} posA
|
|
2201
2208
|
* @param {Vector2} posB
|
|
2202
|
-
* @param {Number} [thickness
|
|
2203
|
-
* @param {Color} [color=
|
|
2209
|
+
* @param {Number} [thickness]
|
|
2210
|
+
* @param {Color} [color=(1,1,1,1)]
|
|
2204
2211
|
* @param {Boolean} [useWebGL=glEnable]
|
|
2205
|
-
* @param {Boolean} [screenSpace=
|
|
2212
|
+
* @param {Boolean} [screenSpace=false]
|
|
2206
2213
|
* @param {CanvasRenderingContext2D} [context]
|
|
2207
2214
|
* @memberof Draw */
|
|
2208
2215
|
function drawLine(posA, posB, thickness=.1, color, useWebGL, screenSpace, context)
|
|
@@ -2218,7 +2225,7 @@ function drawLine(posA, posB, thickness=.1, color, useWebGL, screenSpace, contex
|
|
|
2218
2225
|
* @param {Number} angle
|
|
2219
2226
|
* @param {Boolean} mirror
|
|
2220
2227
|
* @param {Function} drawFunction
|
|
2221
|
-
* @param {Boolean} [screenSpace=
|
|
2228
|
+
* @param {Boolean} [screenSpace=false]
|
|
2222
2229
|
* @param {CanvasRenderingContext2D} [context=mainContext]
|
|
2223
2230
|
* @memberof Draw */
|
|
2224
2231
|
function drawCanvas2D(pos, size, angle, mirror, drawFunction, screenSpace, context=mainContext)
|
|
@@ -2238,13 +2245,13 @@ function drawCanvas2D(pos, size, angle, mirror, drawFunction, screenSpace, conte
|
|
|
2238
2245
|
}
|
|
2239
2246
|
|
|
2240
2247
|
/** Enable normal or additive blend mode
|
|
2241
|
-
* @param {Boolean} [additive
|
|
2248
|
+
* @param {Boolean} [additive]
|
|
2242
2249
|
* @param {Boolean} [useWebGL=glEnable]
|
|
2243
2250
|
* @param {CanvasRenderingContext2D} [context=mainContext]
|
|
2244
2251
|
* @memberof Draw */
|
|
2245
2252
|
function setBlendMode(additive, useWebGL=glEnable, context)
|
|
2246
2253
|
{
|
|
2247
|
-
ASSERT(!context || !useWebGL
|
|
2254
|
+
ASSERT(!context || !useWebGL, 'context only supported in canvas 2D mode');
|
|
2248
2255
|
if (useWebGL)
|
|
2249
2256
|
glAdditive = additive;
|
|
2250
2257
|
else
|
|
@@ -2259,11 +2266,11 @@ function setBlendMode(additive, useWebGL=glEnable, context)
|
|
|
2259
2266
|
* Automatically splits new lines into rows
|
|
2260
2267
|
* @param {String} text
|
|
2261
2268
|
* @param {Vector2} pos
|
|
2262
|
-
* @param {Number} [size
|
|
2263
|
-
* @param {Color} [color=
|
|
2264
|
-
* @param {Number} [lineWidth
|
|
2265
|
-
* @param {Color} [lineColor=
|
|
2266
|
-
* @param {
|
|
2269
|
+
* @param {Number} [size]
|
|
2270
|
+
* @param {Color} [color=(1,1,1,1)]
|
|
2271
|
+
* @param {Number} [lineWidth]
|
|
2272
|
+
* @param {Color} [lineColor=(0,0,0,1)]
|
|
2273
|
+
* @param {CanvasTextAlign} [textAlign='center']
|
|
2267
2274
|
* @param {String} [font=fontDefault]
|
|
2268
2275
|
* @param {CanvasRenderingContext2D} [context=overlayContext]
|
|
2269
2276
|
* @memberof Draw */
|
|
@@ -2276,19 +2283,19 @@ function drawText(text, pos, size=1, color, lineWidth=0, lineColor, textAlign, f
|
|
|
2276
2283
|
* Automatically splits new lines into rows
|
|
2277
2284
|
* @param {String} text
|
|
2278
2285
|
* @param {Vector2} pos
|
|
2279
|
-
* @param {Number} [size
|
|
2280
|
-
* @param {Color} [color=
|
|
2281
|
-
* @param {Number} [lineWidth
|
|
2282
|
-
* @param {Color} [lineColor=
|
|
2283
|
-
* @param {
|
|
2286
|
+
* @param {Number} [size]
|
|
2287
|
+
* @param {Color} [color=(1,1,1,1)]
|
|
2288
|
+
* @param {Number} [lineWidth]
|
|
2289
|
+
* @param {Color} [lineColor=(0,0,0,1)]
|
|
2290
|
+
* @param {CanvasTextAlign} [textAlign]
|
|
2284
2291
|
* @param {String} [font=fontDefault]
|
|
2285
2292
|
* @param {CanvasRenderingContext2D} [context=overlayContext]
|
|
2286
2293
|
* @memberof Draw */
|
|
2287
2294
|
function drawTextScreen(text, pos, size=1, color=new Color, lineWidth=0, lineColor=new Color(0,0,0), textAlign='center', font=fontDefault, context=overlayContext)
|
|
2288
2295
|
{
|
|
2289
|
-
context.fillStyle = color;
|
|
2296
|
+
context.fillStyle = color.toString();
|
|
2290
2297
|
context.lineWidth = lineWidth;
|
|
2291
|
-
context.strokeStyle = lineColor;
|
|
2298
|
+
context.strokeStyle = lineColor.toString();
|
|
2292
2299
|
context.textAlign = textAlign;
|
|
2293
2300
|
context.font = size + 'px '+ font;
|
|
2294
2301
|
context.textBaseline = 'middle';
|
|
@@ -2322,9 +2329,9 @@ let engineFontImage;
|
|
|
2322
2329
|
class FontImage
|
|
2323
2330
|
{
|
|
2324
2331
|
/** Create an image font
|
|
2325
|
-
* @param {HTMLImageElement} [image]
|
|
2326
|
-
* @param {Vector2} [tileSize=
|
|
2327
|
-
* @param {Vector2} [paddingSize=
|
|
2332
|
+
* @param {HTMLImageElement} [image] - Image for the font, if undefined default font is used
|
|
2333
|
+
* @param {Vector2} [tileSize=(8,8)] - Size of the font source tiles
|
|
2334
|
+
* @param {Vector2} [paddingSize=(0,1)] - How much extra space to add between characters
|
|
2328
2335
|
* @param {CanvasRenderingContext2D} [context=overlayContext] - context to draw to
|
|
2329
2336
|
*/
|
|
2330
2337
|
constructor(image, tileSize=vec2(8), paddingSize=vec2(0,1), context=overlayContext)
|
|
@@ -2353,7 +2360,7 @@ class FontImage
|
|
|
2353
2360
|
/** Draw text in screen space using the image font
|
|
2354
2361
|
* @param {String} text
|
|
2355
2362
|
* @param {Vector2} pos
|
|
2356
|
-
* @param {Number} [scale
|
|
2363
|
+
* @param {Number} [scale]
|
|
2357
2364
|
* @param {Boolean} [center]
|
|
2358
2365
|
*/
|
|
2359
2366
|
drawTextScreen(text, pos, scale=4, center)
|
|
@@ -2371,7 +2378,7 @@ class FontImage
|
|
|
2371
2378
|
for(let j=line.length; j--;)
|
|
2372
2379
|
{
|
|
2373
2380
|
// draw each character
|
|
2374
|
-
let charCode = line[j].charCodeAt();
|
|
2381
|
+
let charCode = line[j].charCodeAt(0);
|
|
2375
2382
|
if (charCode < 32 || charCode > 127)
|
|
2376
2383
|
charCode = 127; // unknown character
|
|
2377
2384
|
|
|
@@ -2395,7 +2402,7 @@ class FontImage
|
|
|
2395
2402
|
/** Returns true if fullscreen mode is active
|
|
2396
2403
|
* @return {Boolean}
|
|
2397
2404
|
* @memberof Draw */
|
|
2398
|
-
function isFullscreen() { return document.fullscreenElement; }
|
|
2405
|
+
function isFullscreen() { return !!document.fullscreenElement; }
|
|
2399
2406
|
|
|
2400
2407
|
/** Toggle fullsceen mode
|
|
2401
2408
|
* @memberof Draw */
|
|
@@ -2421,28 +2428,37 @@ function toggleFullscreen()
|
|
|
2421
2428
|
|
|
2422
2429
|
|
|
2423
2430
|
/** Returns true if device key is down
|
|
2424
|
-
* @param {Number} key
|
|
2425
|
-
* @param {Number} [device
|
|
2431
|
+
* @param {String|Number} key
|
|
2432
|
+
* @param {Number} [device]
|
|
2426
2433
|
* @return {Boolean}
|
|
2427
2434
|
* @memberof Input */
|
|
2428
2435
|
function keyIsDown(key, device=0)
|
|
2429
|
-
{
|
|
2436
|
+
{
|
|
2437
|
+
ASSERT(device > 0 || typeof key !== 'number' || key < 3, 'use code string for keyboard');
|
|
2438
|
+
return inputData[device] && !!(inputData[device][key] & 1);
|
|
2439
|
+
}
|
|
2430
2440
|
|
|
2431
2441
|
/** Returns true if device key was pressed this frame
|
|
2432
|
-
* @param {Number} key
|
|
2433
|
-
* @param {Number} [device
|
|
2442
|
+
* @param {String|Number} key
|
|
2443
|
+
* @param {Number} [device]
|
|
2434
2444
|
* @return {Boolean}
|
|
2435
2445
|
* @memberof Input */
|
|
2436
2446
|
function keyWasPressed(key, device=0)
|
|
2437
|
-
{
|
|
2447
|
+
{
|
|
2448
|
+
ASSERT(device > 0 || typeof key !== 'number' || key < 3, 'use code string for keyboard');
|
|
2449
|
+
return inputData[device] && !!(inputData[device][key] & 2);
|
|
2450
|
+
}
|
|
2438
2451
|
|
|
2439
2452
|
/** Returns true if device key was released this frame
|
|
2440
|
-
* @param {Number} key
|
|
2441
|
-
* @param {Number} [device
|
|
2453
|
+
* @param {String|Number} key
|
|
2454
|
+
* @param {Number} [device]
|
|
2442
2455
|
* @return {Boolean}
|
|
2443
2456
|
* @memberof Input */
|
|
2444
2457
|
function keyWasReleased(key, device=0)
|
|
2445
|
-
{
|
|
2458
|
+
{
|
|
2459
|
+
ASSERT(device > 0 || typeof key !== 'number' || key < 3, 'use code string for keyboard');
|
|
2460
|
+
return inputData[device] && !!(inputData[device][key] & 4);
|
|
2461
|
+
}
|
|
2446
2462
|
|
|
2447
2463
|
/** Clears all input
|
|
2448
2464
|
* @memberof Input */
|
|
@@ -2487,16 +2503,16 @@ let mouseWheel = 0;
|
|
|
2487
2503
|
/** Returns true if user is using gamepad (has more recently pressed a gamepad button)
|
|
2488
2504
|
* @type {Boolean}
|
|
2489
2505
|
* @memberof Input */
|
|
2490
|
-
let isUsingGamepad =
|
|
2506
|
+
let isUsingGamepad = false;
|
|
2491
2507
|
|
|
2492
2508
|
/** Prevents input continuing to the default browser handling (false by default)
|
|
2493
2509
|
* @type {Boolean}
|
|
2494
2510
|
* @memberof Input */
|
|
2495
|
-
let preventDefaultInput =
|
|
2511
|
+
let preventDefaultInput = false;
|
|
2496
2512
|
|
|
2497
2513
|
/** Returns true if gamepad button is down
|
|
2498
2514
|
* @param {Number} button
|
|
2499
|
-
* @param {Number} [gamepad
|
|
2515
|
+
* @param {Number} [gamepad]
|
|
2500
2516
|
* @return {Boolean}
|
|
2501
2517
|
* @memberof Input */
|
|
2502
2518
|
function gamepadIsDown(button, gamepad=0)
|
|
@@ -2504,7 +2520,7 @@ function gamepadIsDown(button, gamepad=0)
|
|
|
2504
2520
|
|
|
2505
2521
|
/** Returns true if gamepad button was pressed
|
|
2506
2522
|
* @param {Number} button
|
|
2507
|
-
* @param {Number} [gamepad
|
|
2523
|
+
* @param {Number} [gamepad]
|
|
2508
2524
|
* @return {Boolean}
|
|
2509
2525
|
* @memberof Input */
|
|
2510
2526
|
function gamepadWasPressed(button, gamepad=0)
|
|
@@ -2512,7 +2528,7 @@ function gamepadWasPressed(button, gamepad=0)
|
|
|
2512
2528
|
|
|
2513
2529
|
/** Returns true if gamepad button was released
|
|
2514
2530
|
* @param {Number} button
|
|
2515
|
-
* @param {Number} [gamepad
|
|
2531
|
+
* @param {Number} [gamepad]
|
|
2516
2532
|
* @return {Boolean}
|
|
2517
2533
|
* @memberof Input */
|
|
2518
2534
|
function gamepadWasReleased(button, gamepad=0)
|
|
@@ -2520,7 +2536,7 @@ function gamepadWasReleased(button, gamepad=0)
|
|
|
2520
2536
|
|
|
2521
2537
|
/** Returns gamepad stick value
|
|
2522
2538
|
* @param {Number} stick
|
|
2523
|
-
* @param {Number} [gamepad
|
|
2539
|
+
* @param {Number} [gamepad]
|
|
2524
2540
|
* @return {Vector2}
|
|
2525
2541
|
* @memberof Input */
|
|
2526
2542
|
function gamepadStick(stick, gamepad=0)
|
|
@@ -2563,9 +2579,10 @@ function inputUpdatePost()
|
|
|
2563
2579
|
if (debug && e.target != document.body) return;
|
|
2564
2580
|
if (!e.repeat)
|
|
2565
2581
|
{
|
|
2566
|
-
|
|
2582
|
+
isUsingGamepad = false;
|
|
2583
|
+
inputData[0][e.code] = 3;
|
|
2567
2584
|
if (inputWASDEmulateDirection)
|
|
2568
|
-
inputData[0][remapKey(e.
|
|
2585
|
+
inputData[0][remapKey(e.code)] = 3;
|
|
2569
2586
|
}
|
|
2570
2587
|
preventDefaultInput && e.preventDefault();
|
|
2571
2588
|
}
|
|
@@ -2573,26 +2590,29 @@ function inputUpdatePost()
|
|
|
2573
2590
|
onkeyup = (e)=>
|
|
2574
2591
|
{
|
|
2575
2592
|
if (debug && e.target != document.body) return;
|
|
2576
|
-
inputData[0][e.
|
|
2593
|
+
inputData[0][e.code] = 4;
|
|
2577
2594
|
if (inputWASDEmulateDirection)
|
|
2578
|
-
inputData[0][remapKey(e.
|
|
2595
|
+
inputData[0][remapKey(e.code)] = 4;
|
|
2579
2596
|
}
|
|
2580
2597
|
|
|
2581
2598
|
// handle remapping wasd keys to directions
|
|
2582
2599
|
function remapKey(c)
|
|
2583
|
-
{
|
|
2600
|
+
{
|
|
2584
2601
|
return inputWASDEmulateDirection ?
|
|
2585
|
-
c
|
|
2602
|
+
c == 'KeyW' ? 'ArrowUp' :
|
|
2603
|
+
c == 'KeyS' ? 'ArrowDown' :
|
|
2604
|
+
c == 'KeyA' ? 'ArrowLeft' :
|
|
2605
|
+
c == 'KeyD' ? 'ArrowRight' : c : c;
|
|
2586
2606
|
}
|
|
2587
2607
|
}
|
|
2588
2608
|
|
|
2589
2609
|
///////////////////////////////////////////////////////////////////////////////
|
|
2590
2610
|
// Mouse event handlers
|
|
2591
2611
|
|
|
2592
|
-
onmousedown = (e)=> {
|
|
2612
|
+
onmousedown = (e)=> {isUsingGamepad = false; inputData[0][e.button] = 3; mousePosScreen = mouseToScreen(e); e.button && e.preventDefault();}
|
|
2593
2613
|
onmouseup = (e)=> inputData[0][e.button] = inputData[0][e.button] & 2 | 4;
|
|
2594
2614
|
onmousemove = (e)=> mousePosScreen = mouseToScreen(e);
|
|
2595
|
-
onwheel
|
|
2615
|
+
onwheel = (e)=> mouseWheel = e.ctrlKey ? 0 : sign(e.deltaY);
|
|
2596
2616
|
oncontextmenu = (e)=> false; // prevent right click menu
|
|
2597
2617
|
|
|
2598
2618
|
// convert a mouse or touch event position to screen space
|
|
@@ -2630,7 +2650,7 @@ function gamepadsUpdate()
|
|
|
2630
2650
|
for (let i=10; i--;)
|
|
2631
2651
|
{
|
|
2632
2652
|
const j = i == 3 ? 2 : i == 2 ? 3 : i; // fix button locations
|
|
2633
|
-
data[j] = touchGamepadButtons[i] ?
|
|
2653
|
+
data[j] = touchGamepadButtons[i] ? gamepadIsDown(j,0) ? 1 : 3 : gamepadIsDown(j,0) ? 4 : 0;
|
|
2634
2654
|
}
|
|
2635
2655
|
}
|
|
2636
2656
|
}
|
|
@@ -2662,18 +2682,23 @@ function gamepadsUpdate()
|
|
|
2662
2682
|
for (let j = gamepad.buttons.length; j--;)
|
|
2663
2683
|
{
|
|
2664
2684
|
const button = gamepad.buttons[j];
|
|
2665
|
-
|
|
2666
|
-
|
|
2667
|
-
|
|
2685
|
+
const wasDown = gamepadIsDown(j,i);
|
|
2686
|
+
data[j] = button.pressed ? wasDown ? 1 : 3 : wasDown ? 4 : 0;
|
|
2687
|
+
isUsingGamepad ||= !i && button.pressed;
|
|
2668
2688
|
}
|
|
2669
2689
|
|
|
2670
2690
|
if (gamepadDirectionEmulateStick)
|
|
2671
2691
|
{
|
|
2672
2692
|
// copy dpad to left analog stick when pressed
|
|
2673
|
-
const dpad = vec2(
|
|
2693
|
+
const dpad = vec2(
|
|
2694
|
+
(gamepadIsDown(15,i)&&1) - (gamepadIsDown(14,i)&&1),
|
|
2695
|
+
(gamepadIsDown(12,i)&&1) - (gamepadIsDown(13,i)&&1));
|
|
2674
2696
|
if (dpad.lengthSquared())
|
|
2675
2697
|
sticks[0] = dpad.clampLength();
|
|
2676
2698
|
}
|
|
2699
|
+
|
|
2700
|
+
// disable touch gamepad if using real gamepad
|
|
2701
|
+
touchGamepadEnable && isUsingGamepad && touchGamepadTimer.unset();
|
|
2677
2702
|
}
|
|
2678
2703
|
}
|
|
2679
2704
|
}
|
|
@@ -2681,9 +2706,9 @@ function gamepadsUpdate()
|
|
|
2681
2706
|
///////////////////////////////////////////////////////////////////////////////
|
|
2682
2707
|
|
|
2683
2708
|
/** Pulse the vibration hardware if it exists
|
|
2684
|
-
* @param {Number} [pattern
|
|
2709
|
+
* @param {Number|Array} [pattern] - single value in ms or vibration interval array
|
|
2685
2710
|
* @memberof Input */
|
|
2686
|
-
function vibrate(pattern)
|
|
2711
|
+
function vibrate(pattern=100)
|
|
2687
2712
|
{ vibrateEnable && navigator && navigator.vibrate && navigator.vibrate(pattern); }
|
|
2688
2713
|
|
|
2689
2714
|
/** Cancel any ongoing vibration
|
|
@@ -2701,29 +2726,28 @@ const isTouchDevice = window.ontouchstart !== undefined;
|
|
|
2701
2726
|
if (isTouchDevice)
|
|
2702
2727
|
{
|
|
2703
2728
|
// override mouse events
|
|
2704
|
-
let wasTouching
|
|
2729
|
+
let wasTouching;
|
|
2705
2730
|
onmousedown = onmouseup = ()=> 0;
|
|
2706
2731
|
|
|
2707
2732
|
// handle all touch events the same way
|
|
2708
2733
|
ontouchstart = ontouchmove = ontouchend = (e)=>
|
|
2709
2734
|
{
|
|
2710
|
-
e.button = 0; // all touches are left click
|
|
2711
|
-
|
|
2712
2735
|
// fix stalled audio on mobile
|
|
2713
2736
|
if (soundEnable)
|
|
2714
2737
|
audioContext ? audioContext.resume() : zzfx(0);
|
|
2715
2738
|
|
|
2716
2739
|
// check if touching and pass to mouse events
|
|
2717
2740
|
const touching = e.touches.length;
|
|
2741
|
+
const button = 0; // all touches are left mouse button
|
|
2718
2742
|
if (touching)
|
|
2719
2743
|
{
|
|
2720
2744
|
// set event pos and pass it along
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
wasTouching ?
|
|
2745
|
+
const p = vec2(e.touches[0].clientX, e.touches[0].clientY);
|
|
2746
|
+
mousePosScreen = mouseToScreen(p);
|
|
2747
|
+
wasTouching ? isUsingGamepad = false : inputData[0][button] = 3;
|
|
2724
2748
|
}
|
|
2725
2749
|
else if (wasTouching)
|
|
2726
|
-
|
|
2750
|
+
inputData[0][button] = inputData[0][button] & 2 | 4;
|
|
2727
2751
|
|
|
2728
2752
|
// set was touching
|
|
2729
2753
|
wasTouching = touching;
|
|
@@ -2804,8 +2828,8 @@ function createTouchGamepad()
|
|
|
2804
2828
|
}
|
|
2805
2829
|
|
|
2806
2830
|
// call default touch handler and set to using gamepad
|
|
2807
|
-
touchHandler(e);
|
|
2808
|
-
isUsingGamepad =
|
|
2831
|
+
touchHandler.bind(window)(e);
|
|
2832
|
+
isUsingGamepad = true;
|
|
2809
2833
|
|
|
2810
2834
|
// must return true so the document will get focus
|
|
2811
2835
|
return true;
|
|
@@ -2819,7 +2843,7 @@ function touchGamepadRender()
|
|
|
2819
2843
|
return;
|
|
2820
2844
|
|
|
2821
2845
|
// fade off when not touching or paused
|
|
2822
|
-
const alpha = percent(touchGamepadTimer, 4, 3);
|
|
2846
|
+
const alpha = percent(touchGamepadTimer.get(), 4, 3);
|
|
2823
2847
|
if (!alpha || paused)
|
|
2824
2848
|
return;
|
|
2825
2849
|
|
|
@@ -2923,13 +2947,13 @@ class Sound
|
|
|
2923
2947
|
|
|
2924
2948
|
/** Play the sound
|
|
2925
2949
|
* @param {Vector2} [pos] - World space position to play the sound, sound is not attenuated if null
|
|
2926
|
-
* @param {Number} [volume
|
|
2927
|
-
* @param {Number} [pitch
|
|
2928
|
-
* @param {Number} [randomnessScale
|
|
2929
|
-
* @param {Boolean} [loop
|
|
2950
|
+
* @param {Number} [volume] - How much to scale volume by (in addition to range fade)
|
|
2951
|
+
* @param {Number} [pitch] - How much to scale pitch by (also adjusted by this.randomness)
|
|
2952
|
+
* @param {Number} [randomnessScale] - How much to scale randomness
|
|
2953
|
+
* @param {Boolean} [loop] - Should the sound loop
|
|
2930
2954
|
* @return {AudioBufferSourceNode} - The audio source node
|
|
2931
2955
|
*/
|
|
2932
|
-
play(pos, volume=1, pitch=1, randomnessScale=1, loop=
|
|
2956
|
+
play(pos, volume=1, pitch=1, randomnessScale=1, loop=false)
|
|
2933
2957
|
{
|
|
2934
2958
|
if (!soundEnable || !this.sampleChannels) return;
|
|
2935
2959
|
|
|
@@ -2962,8 +2986,13 @@ class Sound
|
|
|
2962
2986
|
{
|
|
2963
2987
|
if (this.source)
|
|
2964
2988
|
this.source.stop();
|
|
2965
|
-
this.source =
|
|
2989
|
+
this.source = undefined;
|
|
2966
2990
|
}
|
|
2991
|
+
|
|
2992
|
+
/** Get source of most recent instance of this sound that was played
|
|
2993
|
+
* @return {AudioBufferSourceNode}
|
|
2994
|
+
*/
|
|
2995
|
+
getSource() { return this.source; }
|
|
2967
2996
|
|
|
2968
2997
|
/** Play the sound as a note with a semitone offset
|
|
2969
2998
|
* @param {Number} semitoneOffset - How many semitones to offset pitch
|
|
@@ -2979,12 +3008,7 @@ class Sound
|
|
|
2979
3008
|
*/
|
|
2980
3009
|
getDuration()
|
|
2981
3010
|
{ return this.sampleChannels && this.sampleChannels[0].length / this.sampleRate; }
|
|
2982
|
-
|
|
2983
|
-
/** Check if the last instance of this sound is playing
|
|
2984
|
-
* @return {Boolean} - True if the sound is playing
|
|
2985
|
-
*/
|
|
2986
|
-
isPlaying() { return this.source && !this.source.ended; }
|
|
2987
|
-
|
|
3011
|
+
|
|
2988
3012
|
/** Check if sound is loading, for sounds fetched from a url
|
|
2989
3013
|
* @return {Boolean} - True if sound is loading and not ready to play
|
|
2990
3014
|
*/
|
|
@@ -3005,13 +3029,13 @@ class SoundWave extends Sound
|
|
|
3005
3029
|
{
|
|
3006
3030
|
/** Create a sound object and cache the wave file for later use
|
|
3007
3031
|
* @param {String} filename - Filename of audio file to load
|
|
3008
|
-
* @param {Number} [randomness
|
|
3032
|
+
* @param {Number} [randomness] - How much to randomize frequency each time sound plays
|
|
3009
3033
|
* @param {Number} [range=soundDefaultRange] - World space max range of sound, will not play if camera is farther away
|
|
3010
3034
|
* @param {Number} [taper=soundDefaultTaper] - At what percentage of range should it start tapering off
|
|
3011
3035
|
*/
|
|
3012
3036
|
constructor(filename, randomness=0, range, taper)
|
|
3013
3037
|
{
|
|
3014
|
-
super(
|
|
3038
|
+
super(undefined, range, taper);
|
|
3015
3039
|
this.randomness = randomness;
|
|
3016
3040
|
|
|
3017
3041
|
if (!soundEnable) return;
|
|
@@ -3065,11 +3089,11 @@ let soundDecoderContext; // audio context used only to decode audio files
|
|
|
3065
3089
|
class Music extends Sound
|
|
3066
3090
|
{
|
|
3067
3091
|
/** Create a music object and cache the zzfx music samples for later use
|
|
3068
|
-
* @param {Array} zzfxMusic - Array of zzfx music parameters
|
|
3092
|
+
* @param {[Array, Array, Array, Number]} zzfxMusic - Array of zzfx music parameters
|
|
3069
3093
|
*/
|
|
3070
3094
|
constructor(zzfxMusic)
|
|
3071
3095
|
{
|
|
3072
|
-
super();
|
|
3096
|
+
super(undefined);
|
|
3073
3097
|
|
|
3074
3098
|
if (!soundEnable) return;
|
|
3075
3099
|
this.randomness = 0;
|
|
@@ -3082,17 +3106,17 @@ class Music extends Sound
|
|
|
3082
3106
|
* @param {Boolean} [loop=1] - True if the music should loop
|
|
3083
3107
|
* @return {AudioBufferSourceNode} - The audio source node
|
|
3084
3108
|
*/
|
|
3085
|
-
playMusic(volume, loop =
|
|
3086
|
-
{ return super.play(
|
|
3109
|
+
playMusic(volume, loop = false)
|
|
3110
|
+
{ return super.play(undefined, volume, 1, 1, loop); }
|
|
3087
3111
|
}
|
|
3088
3112
|
|
|
3089
3113
|
/** Play an mp3, ogg, or wav audio from a local file or url
|
|
3090
3114
|
* @param {String} url - Location of sound file to play
|
|
3091
|
-
* @param {Number} [volume
|
|
3092
|
-
* @param {Boolean} [loop
|
|
3115
|
+
* @param {Number} [volume] - How much to scale volume by
|
|
3116
|
+
* @param {Boolean} [loop] - True if the music should loop
|
|
3093
3117
|
* @return {HTMLAudioElement} - The audio element for this sound
|
|
3094
3118
|
* @memberof Audio */
|
|
3095
|
-
function playAudioFile(url, volume=1, loop=
|
|
3119
|
+
function playAudioFile(url, volume=1, loop=false)
|
|
3096
3120
|
{
|
|
3097
3121
|
if (!soundEnable) return;
|
|
3098
3122
|
|
|
@@ -3106,9 +3130,9 @@ function playAudioFile(url, volume=1, loop=1)
|
|
|
3106
3130
|
/** Speak text with passed in settings
|
|
3107
3131
|
* @param {String} text - The text to speak
|
|
3108
3132
|
* @param {String} [language] - The language/accent to use (examples: en, it, ru, ja, zh)
|
|
3109
|
-
* @param {Number} [volume
|
|
3110
|
-
* @param {Number} [rate
|
|
3111
|
-
* @param {Number} [pitch
|
|
3133
|
+
* @param {Number} [volume] - How much to scale volume by
|
|
3134
|
+
* @param {Number} [rate] - How quickly to speak
|
|
3135
|
+
* @param {Number} [pitch] - How much to change the pitch by
|
|
3112
3136
|
* @return {SpeechSynthesisUtterance} - The utterance that was spoken
|
|
3113
3137
|
* @memberof Audio */
|
|
3114
3138
|
function speak(text, language='', volume=1, rate=1, pitch=1)
|
|
@@ -3135,7 +3159,7 @@ function speakStop() {speechSynthesis && speechSynthesis.cancel();}
|
|
|
3135
3159
|
|
|
3136
3160
|
/** Get frequency of a note on a musical scale
|
|
3137
3161
|
* @param {Number} semitoneOffset - How many semitones away from the root note
|
|
3138
|
-
* @param {Number} [
|
|
3162
|
+
* @param {Number} [rootFrequency=220] - Frequency at semitone offset 0
|
|
3139
3163
|
* @return {Number} - The frequency of the note
|
|
3140
3164
|
* @memberof Audio */
|
|
3141
3165
|
function getNoteFrequency(semitoneOffset, rootFrequency=220)
|
|
@@ -3149,14 +3173,14 @@ let audioContext;
|
|
|
3149
3173
|
|
|
3150
3174
|
/** Play cached audio samples with given settings
|
|
3151
3175
|
* @param {Array} sampleChannels - Array of arrays of samples to play (for stereo playback)
|
|
3152
|
-
* @param {Number} [volume
|
|
3153
|
-
* @param {Number} [rate
|
|
3154
|
-
* @param {Number} [pan
|
|
3155
|
-
* @param {Boolean} [loop
|
|
3176
|
+
* @param {Number} [volume] - How much to scale volume by
|
|
3177
|
+
* @param {Number} [rate] - The playback rate to use
|
|
3178
|
+
* @param {Number} [pan] - How much to apply stereo panning
|
|
3179
|
+
* @param {Boolean} [loop] - True if the sound should loop when it reaches the end
|
|
3156
3180
|
* @param {Number} [sampleRate=44100] - Sample rate for the sound
|
|
3157
3181
|
* @return {AudioBufferSourceNode} - The audio node of the sound played
|
|
3158
3182
|
* @memberof Audio */
|
|
3159
|
-
function playSamples(sampleChannels, volume=1, rate=1, pan=0, loop=
|
|
3183
|
+
function playSamples(sampleChannels, volume=1, rate=1, pan=0, loop=false, sampleRate=zzfxR)
|
|
3160
3184
|
{
|
|
3161
3185
|
if (!soundEnable) return;
|
|
3162
3186
|
|
|
@@ -3196,7 +3220,7 @@ function playSamples(sampleChannels, volume=1, rate=1, pan=0, loop=0, sampleRate
|
|
|
3196
3220
|
}
|
|
3197
3221
|
|
|
3198
3222
|
///////////////////////////////////////////////////////////////////////////////
|
|
3199
|
-
// ZzFXMicro - Zuper Zmall Zound Zynth - v1.3.
|
|
3223
|
+
// ZzFXMicro - Zuper Zmall Zound Zynth - v1.3.1 by Frank Force
|
|
3200
3224
|
|
|
3201
3225
|
/** Generate and play a ZzFX sound
|
|
3202
3226
|
*
|
|
@@ -3212,27 +3236,27 @@ function zzfx(...zzfxSound) { return playSamples([zzfxG(...zzfxSound)]); }
|
|
|
3212
3236
|
const zzfxR = 44100;
|
|
3213
3237
|
|
|
3214
3238
|
/** Generate samples for a ZzFX sound
|
|
3215
|
-
* @param {Number} [volume
|
|
3216
|
-
* @param {Number} [randomness
|
|
3217
|
-
* @param {Number} [frequency
|
|
3218
|
-
* @param {Number} [attack
|
|
3219
|
-
* @param {Number} [sustain
|
|
3220
|
-
* @param {Number} [release
|
|
3221
|
-
* @param {Number} [shape
|
|
3222
|
-
* @param {Number} [shapeCurve
|
|
3223
|
-
* @param {Number} [slide
|
|
3224
|
-
* @param {Number} [deltaSlide
|
|
3225
|
-
* @param {Number} [pitchJump
|
|
3226
|
-
* @param {Number} [pitchJumpTime
|
|
3227
|
-
* @param {Number} [repeatTime
|
|
3228
|
-
* @param {Number} [noise
|
|
3229
|
-
* @param {Number} [modulation
|
|
3230
|
-
* @param {Number} [bitCrush
|
|
3231
|
-
* @param {Number} [delay
|
|
3232
|
-
* @param {Number} [sustainVolume
|
|
3233
|
-
* @param {Number} [decay
|
|
3234
|
-
* @param {Number} [tremolo
|
|
3235
|
-
* @param {Number} [filter
|
|
3239
|
+
* @param {Number} [volume] - Volume scale (percent)
|
|
3240
|
+
* @param {Number} [randomness] - How much to randomize frequency (percent Hz)
|
|
3241
|
+
* @param {Number} [frequency] - Frequency of sound (Hz)
|
|
3242
|
+
* @param {Number} [attack] - Attack time, how fast sound starts (seconds)
|
|
3243
|
+
* @param {Number} [sustain] - Sustain time, how long sound holds (seconds)
|
|
3244
|
+
* @param {Number} [release] - Release time, how fast sound fades out (seconds)
|
|
3245
|
+
* @param {Number} [shape] - Shape of the sound wave
|
|
3246
|
+
* @param {Number} [shapeCurve] - Squarenes of wave (0=square, 1=normal, 2=pointy)
|
|
3247
|
+
* @param {Number} [slide] - How much to slide frequency (kHz/s)
|
|
3248
|
+
* @param {Number} [deltaSlide] - How much to change slide (kHz/s/s)
|
|
3249
|
+
* @param {Number} [pitchJump] - Frequency of pitch jump (Hz)
|
|
3250
|
+
* @param {Number} [pitchJumpTime] - Time of pitch jump (seconds)
|
|
3251
|
+
* @param {Number} [repeatTime] - Resets some parameters periodically (seconds)
|
|
3252
|
+
* @param {Number} [noise] - How much random noise to add (percent)
|
|
3253
|
+
* @param {Number} [modulation] - Frequency of modulation wave, negative flips phase (Hz)
|
|
3254
|
+
* @param {Number} [bitCrush] - Resamples at a lower frequency in (samples*100)
|
|
3255
|
+
* @param {Number} [delay] - Overlap sound with itself for reverb and flanger effects (seconds)
|
|
3256
|
+
* @param {Number} [sustainVolume] - Volume level for sustain (percent)
|
|
3257
|
+
* @param {Number} [decay] - Decay time, how long to reach sustain after attack (seconds)
|
|
3258
|
+
* @param {Number} [tremolo] - Trembling effect, rate controlled by repeat time (precent)
|
|
3259
|
+
* @param {Number} [filter] - Filter cutoff frequency, positive for HPF, negative for LPF (Hz)
|
|
3236
3260
|
* @return {Array} - Array of audio samples
|
|
3237
3261
|
* @memberof Audio
|
|
3238
3262
|
*/
|
|
@@ -3280,7 +3304,7 @@ function zzfxG
|
|
|
3280
3304
|
if (!(++c%(bitCrush*100|0))) // bit crush
|
|
3281
3305
|
{
|
|
3282
3306
|
s = shape? shape>1? shape>2? shape>3? // wave shape
|
|
3283
|
-
Math.sin(t
|
|
3307
|
+
Math.sin(t**3) : // 4 noise
|
|
3284
3308
|
clamp(Math.tan(t),1,-1): // 3 tan
|
|
3285
3309
|
1-(2*t/PI2%2+2)%2: // 2 saw
|
|
3286
3310
|
1-4*abs(Math.round(t/PI2)-t/PI2): // 1 triangle
|
|
@@ -3337,7 +3361,7 @@ function zzfxG
|
|
|
3337
3361
|
* @param {Array} instruments - Array of ZzFX sound paramaters
|
|
3338
3362
|
* @param {Array} patterns - Array of pattern data
|
|
3339
3363
|
* @param {Array} sequence - Array of pattern indexes
|
|
3340
|
-
* @param {Number} [BPM
|
|
3364
|
+
* @param {Number} [BPM] - Playback speed of the song in BPM
|
|
3341
3365
|
* @return {Array} - Left and right channel sample data
|
|
3342
3366
|
* @memberof Audio */
|
|
3343
3367
|
function zzfxM(instruments, patterns, sequence, BPM = 125)
|
|
@@ -3376,10 +3400,10 @@ function zzfxM(instruments, patterns, sequence, BPM = 125)
|
|
|
3376
3400
|
patternChannel = patterns[patternIndex][channelIndex] || [0, 0, 0];
|
|
3377
3401
|
|
|
3378
3402
|
// check if there are more channels
|
|
3379
|
-
hasMore
|
|
3403
|
+
hasMore |= patterns[patternIndex][channelIndex]&&1;
|
|
3380
3404
|
|
|
3381
3405
|
// get next offset, use the length of first channel
|
|
3382
|
-
nextSampleOffset = outSampleOffset + (patterns[patternIndex][0].length - 2 -
|
|
3406
|
+
nextSampleOffset = outSampleOffset + (patterns[patternIndex][0].length - 2 - (notFirstBeat?0:1)) * beatLength;
|
|
3383
3407
|
// for each beat in pattern, plus one extra if end of sequence
|
|
3384
3408
|
isSequenceEnd = sequenceIndex == sequence.length - 1;
|
|
3385
3409
|
for (i = 2, k = outSampleOffset; i < patternChannel.length + isSequenceEnd; notFirstBeat = ++i) {
|
|
@@ -3395,7 +3419,7 @@ function zzfxM(instruments, patterns, sequence, BPM = 125)
|
|
|
3395
3419
|
for (j = 0; j < beatLength && notFirstBeat;
|
|
3396
3420
|
|
|
3397
3421
|
// fade off attenuation at end of beat if stopping note, prevents clicking
|
|
3398
|
-
j++ > beatLength - 99 && stop
|
|
3422
|
+
j++ > beatLength - 99 && stop && attenuation < 1? attenuation += 1 / 99 : 0
|
|
3399
3423
|
) {
|
|
3400
3424
|
// copy sample to stereo buffers with panning
|
|
3401
3425
|
sample = (1 - attenuation) * sampleBuffer[sampleOffset++] / 2 || 0;
|
|
@@ -3471,7 +3495,7 @@ function initTileCollision(size)
|
|
|
3471
3495
|
|
|
3472
3496
|
/** Set tile collision data
|
|
3473
3497
|
* @param {Vector2} pos
|
|
3474
|
-
* @param {Number} [data
|
|
3498
|
+
* @param {Number} [data]
|
|
3475
3499
|
* @memberof TileCollision */
|
|
3476
3500
|
function setTileCollisionData(pos, data=0)
|
|
3477
3501
|
{
|
|
@@ -3489,7 +3513,7 @@ function getTileCollisionData(pos)
|
|
|
3489
3513
|
|
|
3490
3514
|
/** Check if collision with another object should occur
|
|
3491
3515
|
* @param {Vector2} pos
|
|
3492
|
-
* @param {Vector2} [size=
|
|
3516
|
+
* @param {Vector2} [size=(1,1)]
|
|
3493
3517
|
* @param {EngineObject} [object]
|
|
3494
3518
|
* @return {Boolean}
|
|
3495
3519
|
* @memberof TileCollision */
|
|
@@ -3504,7 +3528,7 @@ function tileCollisionTest(pos, size=vec2(), object)
|
|
|
3504
3528
|
{
|
|
3505
3529
|
const tileData = tileCollision[y*tileCollisionSize.x+x];
|
|
3506
3530
|
if (tileData && (!object || object.collideWithTile(tileData, vec2(x, y))))
|
|
3507
|
-
return
|
|
3531
|
+
return true;
|
|
3508
3532
|
}
|
|
3509
3533
|
}
|
|
3510
3534
|
|
|
@@ -3570,11 +3594,11 @@ function tileCollisionRaycast(posStart, posEnd, object)
|
|
|
3570
3594
|
class TileLayerData
|
|
3571
3595
|
{
|
|
3572
3596
|
/** Create a tile layer data object, one for each tile in a TileLayer
|
|
3573
|
-
* @param {Number} [tile]
|
|
3574
|
-
* @param {Number} [direction
|
|
3575
|
-
* @param {Boolean} [mirror
|
|
3576
|
-
* @param {Color} [color
|
|
3577
|
-
constructor(tile, direction=0, mirror=
|
|
3597
|
+
* @param {Number} [tile] - The tile to use, untextured if undefined
|
|
3598
|
+
* @param {Number} [direction] - Integer direction of tile, in 90 degree increments
|
|
3599
|
+
* @param {Boolean} [mirror] - If the tile should be mirrored along the x axis
|
|
3600
|
+
* @param {Color} [color] - Color of the tile */
|
|
3601
|
+
constructor(tile, direction=0, mirror=false, color=new Color())
|
|
3578
3602
|
{
|
|
3579
3603
|
/** @property {Number} - The tile to use, untextured if undefined */
|
|
3580
3604
|
this.tile = tile;
|
|
@@ -3587,7 +3611,7 @@ class TileLayerData
|
|
|
3587
3611
|
}
|
|
3588
3612
|
|
|
3589
3613
|
/** Set this tile to clear, it will not be rendered */
|
|
3590
|
-
clear() { this.tile = this.direction = this.mirror =
|
|
3614
|
+
clear() { this.tile = this.direction = 0; this.mirror = false; this.color = new Color; }
|
|
3591
3615
|
}
|
|
3592
3616
|
|
|
3593
3617
|
/**
|
|
@@ -3604,25 +3628,25 @@ class TileLayerData
|
|
|
3604
3628
|
*/
|
|
3605
3629
|
class TileLayer extends EngineObject
|
|
3606
3630
|
{
|
|
3607
|
-
/** Create a tile layer object
|
|
3608
|
-
* @param {Vector2}
|
|
3609
|
-
* @param {Vector2}
|
|
3610
|
-
* @param {TileInfo} [tileInfo]
|
|
3611
|
-
* @param {Vector2}
|
|
3612
|
-
* @param {Number}
|
|
3631
|
+
/** Create a tile layer object
|
|
3632
|
+
* @param {Vector2} [position=(0,0)] - World space position
|
|
3633
|
+
* @param {Vector2} [size=tileCollisionSize] - World space size
|
|
3634
|
+
* @param {TileInfo} [tileInfo] - Tile info for layer
|
|
3635
|
+
* @param {Vector2} [scale=(1,1)] - How much to scale this layer when rendered
|
|
3636
|
+
* @param {Number} [renderOrder] - Objects are sorted by renderOrder
|
|
3613
3637
|
*/
|
|
3614
|
-
constructor(
|
|
3638
|
+
constructor(position, size=tileCollisionSize, tileInfo=tile(), scale=vec2(1), renderOrder=0)
|
|
3615
3639
|
{
|
|
3616
|
-
super(
|
|
3640
|
+
super(position, size, tileInfo, 0, undefined, renderOrder);
|
|
3617
3641
|
|
|
3618
|
-
/** @property {HTMLCanvasElement}
|
|
3642
|
+
/** @property {HTMLCanvasElement} - The canvas used by this tile layer */
|
|
3619
3643
|
this.canvas = document.createElement('canvas');
|
|
3620
3644
|
/** @property {CanvasRenderingContext2D} - The 2D canvas context used by this tile layer */
|
|
3621
3645
|
this.context = this.canvas.getContext('2d');
|
|
3622
|
-
/** @property {Vector2}
|
|
3646
|
+
/** @property {Vector2} - How much to scale this layer when rendered */
|
|
3623
3647
|
this.scale = scale;
|
|
3624
|
-
/** @property {Boolean}
|
|
3625
|
-
this.isOverlay;
|
|
3648
|
+
/** @property {Boolean} - If true this layer will render to overlay canvas and appear above all objects */
|
|
3649
|
+
this.isOverlay = false;
|
|
3626
3650
|
|
|
3627
3651
|
// init tile data
|
|
3628
3652
|
this.data = [];
|
|
@@ -3631,10 +3655,10 @@ constructor(pos, size=tileCollisionSize, tileInfo=tile(), scale=vec2(1), renderO
|
|
|
3631
3655
|
}
|
|
3632
3656
|
|
|
3633
3657
|
/** Set data at a given position in the array
|
|
3634
|
-
* @param {Vector2}
|
|
3635
|
-
* @param {TileLayerData} data
|
|
3636
|
-
* @param {Boolean} [redraw
|
|
3637
|
-
setData(layerPos, data, redraw)
|
|
3658
|
+
* @param {Vector2} layerPos - Local position in array
|
|
3659
|
+
* @param {TileLayerData} data - Data to set
|
|
3660
|
+
* @param {Boolean} [redraw] - Force the tile to redraw if true */
|
|
3661
|
+
setData(layerPos, data, redraw=false)
|
|
3638
3662
|
{
|
|
3639
3663
|
if (layerPos.arrayCheck(this.size))
|
|
3640
3664
|
{
|
|
@@ -3655,7 +3679,7 @@ constructor(pos, size=tileCollisionSize, tileInfo=tile(), scale=vec2(1), renderO
|
|
|
3655
3679
|
// Render the tile layer, called automatically by the engine
|
|
3656
3680
|
render()
|
|
3657
3681
|
{
|
|
3658
|
-
ASSERT(mainContext != this.context
|
|
3682
|
+
ASSERT(mainContext != this.context, 'must call redrawEnd() after drawing tiles');
|
|
3659
3683
|
|
|
3660
3684
|
// flush and copy gl canvas because tile canvas does not use webgl
|
|
3661
3685
|
glEnable && !glOverlay && !this.isOverlay && glCopyToContext(mainContext);
|
|
@@ -3670,33 +3694,38 @@ constructor(pos, size=tileCollisionSize, tileInfo=tile(), scale=vec2(1), renderO
|
|
|
3670
3694
|
}
|
|
3671
3695
|
|
|
3672
3696
|
/** Draw all the tile data to an offscreen canvas
|
|
3673
|
-
* - This may be slow in some browsers
|
|
3674
|
-
*/
|
|
3697
|
+
* - This may be slow in some browsers but only needs to be done once */
|
|
3675
3698
|
redraw()
|
|
3676
3699
|
{
|
|
3677
|
-
this.redrawStart(
|
|
3678
|
-
this.
|
|
3700
|
+
this.redrawStart(true);
|
|
3701
|
+
for (let x = this.size.x; x--;)
|
|
3702
|
+
for (let y = this.size.y; y--;)
|
|
3703
|
+
this.drawTileData(vec2(x,y), false);
|
|
3679
3704
|
this.redrawEnd();
|
|
3680
3705
|
}
|
|
3681
3706
|
|
|
3682
3707
|
/** Call to start the redraw process
|
|
3683
|
-
*
|
|
3684
|
-
|
|
3708
|
+
* - This can be used to manually update small parts of the level
|
|
3709
|
+
* @param {Boolean} [clear] - Should it clear the canvas before drawing */
|
|
3710
|
+
redrawStart(clear=false)
|
|
3685
3711
|
{
|
|
3686
3712
|
// save current render settings
|
|
3713
|
+
/** @type {[HTMLCanvasElement, CanvasRenderingContext2D, Vector2, Vector2, number]} */
|
|
3687
3714
|
this.savedRenderSettings = [mainCanvas, mainContext, mainCanvasSize, cameraPos, cameraScale];
|
|
3688
3715
|
|
|
3689
|
-
//
|
|
3716
|
+
// use webgl rendering system to render the tiles if enabled
|
|
3717
|
+
// this works by temporally taking control of the rendering system
|
|
3690
3718
|
mainCanvas = this.canvas;
|
|
3691
3719
|
mainContext = this.context;
|
|
3720
|
+
mainCanvasSize = this.size.multiply(this.tileInfo.size);
|
|
3692
3721
|
cameraPos = this.size.scale(.5);
|
|
3693
3722
|
cameraScale = this.tileInfo.size.x;
|
|
3694
3723
|
|
|
3695
3724
|
if (clear)
|
|
3696
3725
|
{
|
|
3697
3726
|
// clear and set size
|
|
3698
|
-
mainCanvas.width =
|
|
3699
|
-
mainCanvas.height =
|
|
3727
|
+
mainCanvas.width = mainCanvasSize.x;
|
|
3728
|
+
mainCanvas.height = mainCanvasSize.y;
|
|
3700
3729
|
}
|
|
3701
3730
|
|
|
3702
3731
|
// begin a new render for the tile canvas
|
|
@@ -3706,40 +3735,41 @@ constructor(pos, size=tileCollisionSize, tileInfo=tile(), scale=vec2(1), renderO
|
|
|
3706
3735
|
/** Call to end the redraw process */
|
|
3707
3736
|
redrawEnd()
|
|
3708
3737
|
{
|
|
3709
|
-
ASSERT(mainContext == this.context
|
|
3710
|
-
glEnable && glCopyToContext(mainContext,
|
|
3738
|
+
ASSERT(mainContext == this.context, 'must call redrawStart() before drawing tiles');
|
|
3739
|
+
glEnable && glCopyToContext(mainContext, true);
|
|
3711
3740
|
//debugSaveCanvas(this.canvas);
|
|
3712
3741
|
|
|
3713
3742
|
// set stuff back to normal
|
|
3714
3743
|
[mainCanvas, mainContext, mainCanvasSize, cameraPos, cameraScale] = this.savedRenderSettings;
|
|
3715
3744
|
}
|
|
3716
3745
|
|
|
3717
|
-
/** Draw the tile at a given position
|
|
3718
|
-
*
|
|
3719
|
-
|
|
3746
|
+
/** Draw the tile at a given position in the tile grid
|
|
3747
|
+
* This can be used to clear out tiles when they are destroyed
|
|
3748
|
+
* Tiles can also be redrawn if isinde a redrawStart/End block
|
|
3749
|
+
* @param {Vector2} layerPos
|
|
3750
|
+
* @param {Boolean} [clear] - should the old tile be cleared out
|
|
3751
|
+
*/
|
|
3752
|
+
drawTileData(layerPos, clear=true)
|
|
3720
3753
|
{
|
|
3721
|
-
//
|
|
3722
|
-
const
|
|
3723
|
-
|
|
3754
|
+
// clear out where the tile was, for full opaque tiles this can be skipped
|
|
3755
|
+
const s = this.tileInfo.size;
|
|
3756
|
+
if (clear)
|
|
3757
|
+
{
|
|
3758
|
+
const pos = layerPos.multiply(s);
|
|
3759
|
+
this.context.clearRect(pos.x, this.canvas.height-pos.y, s.x, -s.y);
|
|
3760
|
+
}
|
|
3724
3761
|
|
|
3725
3762
|
// draw the tile if not undefined
|
|
3726
3763
|
const d = this.getData(layerPos);
|
|
3727
3764
|
if (d.tile != undefined)
|
|
3728
3765
|
{
|
|
3729
|
-
|
|
3730
|
-
|
|
3766
|
+
const pos = this.pos.add(layerPos).add(vec2(.5));
|
|
3767
|
+
ASSERT(mainContext == this.context, 'must call redrawStart() before drawing tiles');
|
|
3768
|
+
const tileInfo = tile(d.tile, s, this.tileInfo.textureIndex);
|
|
3731
3769
|
drawTile(pos, vec2(1), tileInfo, d.color, d.direction*PI/2, d.mirror);
|
|
3732
3770
|
}
|
|
3733
3771
|
}
|
|
3734
3772
|
|
|
3735
|
-
/** Draw all the tiles in this layer */
|
|
3736
|
-
drawAllTileData()
|
|
3737
|
-
{
|
|
3738
|
-
for (let x = this.size.x; x--;)
|
|
3739
|
-
for (let y = this.size.y; y--;)
|
|
3740
|
-
this.drawTileData(vec2(x,y));
|
|
3741
|
-
}
|
|
3742
|
-
|
|
3743
3773
|
/** Draw directly to the 2D canvas in world space (bipass webgl)
|
|
3744
3774
|
* @param {Vector2} pos
|
|
3745
3775
|
* @param {Vector2} size
|
|
@@ -3759,11 +3789,11 @@ constructor(pos, size=tileCollisionSize, tileInfo=tile(), scale=vec2(1), renderO
|
|
|
3759
3789
|
context.restore();
|
|
3760
3790
|
}
|
|
3761
3791
|
|
|
3762
|
-
/** Draw a tile directly onto the layer canvas
|
|
3792
|
+
/** Draw a tile directly onto the layer canvas in world space
|
|
3763
3793
|
* @param {Vector2} pos
|
|
3764
|
-
* @param {Vector2} [size=
|
|
3794
|
+
* @param {Vector2} [size=(1,1)]
|
|
3765
3795
|
* @param {TileInfo} [tileInfo]
|
|
3766
|
-
* @param {Color} [color=
|
|
3796
|
+
* @param {Color} [color=(1,1,1,1)]
|
|
3767
3797
|
* @param {Number} [angle=0]
|
|
3768
3798
|
* @param {Boolean} [mirror=0] */
|
|
3769
3799
|
drawTile(pos, size=vec2(1), tileInfo, color=new Color, angle, mirror)
|
|
@@ -3788,13 +3818,13 @@ constructor(pos, size=tileCollisionSize, tileInfo=tile(), scale=vec2(1), renderO
|
|
|
3788
3818
|
});
|
|
3789
3819
|
}
|
|
3790
3820
|
|
|
3791
|
-
/** Draw a rectangle directly onto the layer canvas
|
|
3821
|
+
/** Draw a rectangle directly onto the layer canvas in world space
|
|
3792
3822
|
* @param {Vector2} pos
|
|
3793
|
-
* @param {Vector2} [size=
|
|
3794
|
-
* @param {Color} [color=
|
|
3823
|
+
* @param {Vector2} [size=(1,1)]
|
|
3824
|
+
* @param {Color} [color=(1,1,1,1)]
|
|
3795
3825
|
* @param {Number} [angle=0] */
|
|
3796
3826
|
drawRect(pos, size, color, angle)
|
|
3797
|
-
{ this.drawTile(pos, size,
|
|
3827
|
+
{ this.drawTile(pos, size, undefined, color, angle); }
|
|
3798
3828
|
}
|
|
3799
3829
|
/**
|
|
3800
3830
|
* LittleJS Particle System
|
|
@@ -3823,36 +3853,36 @@ class ParticleEmitter extends EngineObject
|
|
|
3823
3853
|
{
|
|
3824
3854
|
/** Create a particle system with the given settings
|
|
3825
3855
|
* @param {Vector2} position - World space position of the emitter
|
|
3826
|
-
* @param {Number}
|
|
3827
|
-
* @param {Number|Vector2} [emitSize
|
|
3828
|
-
* @param {Number}
|
|
3829
|
-
* @param {Number}
|
|
3830
|
-
* @param {Number}
|
|
3856
|
+
* @param {Number} [angle] - Angle to emit the particles
|
|
3857
|
+
* @param {Number|Vector2} [emitSize] - World space size of the emitter (float for circle diameter, vec2 for rect)
|
|
3858
|
+
* @param {Number} [emitTime] - How long to stay alive (0 is forever)
|
|
3859
|
+
* @param {Number} [emitRate] - How many particles per second to spawn, does not emit if 0
|
|
3860
|
+
* @param {Number} [emitConeAngle=PI] - Local angle to apply velocity to particles from emitter
|
|
3831
3861
|
* @param {TileInfo} [tileInfo] - Tile info to render particles (undefined is untextured)
|
|
3832
|
-
* @param {Color}
|
|
3833
|
-
* @param {Color}
|
|
3834
|
-
* @param {Color}
|
|
3835
|
-
* @param {Color}
|
|
3836
|
-
* @param {Number}
|
|
3837
|
-
* @param {Number}
|
|
3838
|
-
* @param {Number}
|
|
3839
|
-
* @param {Number}
|
|
3840
|
-
* @param {Number}
|
|
3841
|
-
* @param {Number}
|
|
3842
|
-
* @param {Number}
|
|
3843
|
-
* @param {Number}
|
|
3844
|
-
* @param {Number}
|
|
3845
|
-
* @param {Number}
|
|
3846
|
-
* @param {Number}
|
|
3847
|
-
* @param {Boolean} [collideTiles
|
|
3848
|
-
* @param {Boolean} [additive
|
|
3849
|
-
* @param {Boolean} [randomColorLinear
|
|
3850
|
-
* @param {Number}
|
|
3851
|
-
* @param {Boolean} [localSpace
|
|
3862
|
+
* @param {Color} [colorStartA=(1,1,1,1)] - Color at start of life 1, randomized between start colors
|
|
3863
|
+
* @param {Color} [colorStartB=(1,1,1,1)] - Color at start of life 2, randomized between start colors
|
|
3864
|
+
* @param {Color} [colorEndA=(1,1,1,0)] - Color at end of life 1, randomized between end colors
|
|
3865
|
+
* @param {Color} [colorEndB=(1,1,1,0)] - Color at end of life 2, randomized between end colors
|
|
3866
|
+
* @param {Number} [particleTime] - How long particles live
|
|
3867
|
+
* @param {Number} [sizeStart] - How big are particles at start
|
|
3868
|
+
* @param {Number} [sizeEnd] - How big are particles at end
|
|
3869
|
+
* @param {Number} [speed] - How fast are particles when spawned
|
|
3870
|
+
* @param {Number} [angleSpeed] - How fast are particles rotating
|
|
3871
|
+
* @param {Number} [damping] - How much to dampen particle speed
|
|
3872
|
+
* @param {Number} [angleDamping] - How much to dampen particle angular speed
|
|
3873
|
+
* @param {Number} [gravityScale] - How much gravity effect particles
|
|
3874
|
+
* @param {Number} [particleConeAngle] - Cone for start particle angle
|
|
3875
|
+
* @param {Number} [fadeRate] - How quick to fade particles at start/end in percent of life
|
|
3876
|
+
* @param {Number} [randomness] - Apply extra randomness percent
|
|
3877
|
+
* @param {Boolean} [collideTiles] - Do particles collide against tiles
|
|
3878
|
+
* @param {Boolean} [additive] - Should particles use addtive blend
|
|
3879
|
+
* @param {Boolean} [randomColorLinear] - Should color be randomized linearly or across each component
|
|
3880
|
+
* @param {Number} [renderOrder] - Render order for particles (additive is above other stuff by default)
|
|
3881
|
+
* @param {Boolean} [localSpace] - Should it be in local space of emitter (world space is default)
|
|
3852
3882
|
*/
|
|
3853
3883
|
constructor
|
|
3854
3884
|
(
|
|
3855
|
-
|
|
3885
|
+
position,
|
|
3856
3886
|
angle,
|
|
3857
3887
|
emitSize = 0,
|
|
3858
3888
|
emitTime = 0,
|
|
@@ -3874,14 +3904,14 @@ class ParticleEmitter extends EngineObject
|
|
|
3874
3904
|
particleConeAngle = PI,
|
|
3875
3905
|
fadeRate = .1,
|
|
3876
3906
|
randomness = .2,
|
|
3877
|
-
collideTiles,
|
|
3878
|
-
additive,
|
|
3879
|
-
randomColorLinear =
|
|
3907
|
+
collideTiles = false,
|
|
3908
|
+
additive = false,
|
|
3909
|
+
randomColorLinear = true,
|
|
3880
3910
|
renderOrder = additive ? 1e9 : 0,
|
|
3881
|
-
localSpace
|
|
3911
|
+
localSpace = false
|
|
3882
3912
|
)
|
|
3883
3913
|
{
|
|
3884
|
-
super(
|
|
3914
|
+
super(position, vec2(), tileInfo, angle, undefined, renderOrder);
|
|
3885
3915
|
|
|
3886
3916
|
// emitter settings
|
|
3887
3917
|
/** @property {Number|Vector2} - World space size of the emitter (float for circle diameter, vec2 for rect) */
|
|
@@ -3930,14 +3960,17 @@ class ParticleEmitter extends EngineObject
|
|
|
3930
3960
|
this.randomness = randomness;
|
|
3931
3961
|
/** @property {Boolean} - Do particles collide against tiles */
|
|
3932
3962
|
this.collideTiles = collideTiles;
|
|
3933
|
-
/** @property {
|
|
3963
|
+
/** @property {Boolean} - Should particles use addtive blend */
|
|
3934
3964
|
this.additive = additive;
|
|
3935
3965
|
/** @property {Boolean} - Should it be in local space of emitter */
|
|
3936
|
-
this.localSpace
|
|
3937
|
-
/** @property {Number} - If
|
|
3966
|
+
this.localSpace = localSpace;
|
|
3967
|
+
/** @property {Number} - If non zero the partile is drawn as a trail, stretched in the drection of velocity */
|
|
3938
3968
|
this.trailScale = 0;
|
|
3939
|
-
|
|
3940
|
-
|
|
3969
|
+
/** @property {Function} - Callback when particle is destroyed */
|
|
3970
|
+
this.particleDestroyCallback = undefined;
|
|
3971
|
+
/** @property {Function} - Callback when particle is created */
|
|
3972
|
+
this.particleCreateCallback = undefined;
|
|
3973
|
+
/** @property {Number} - Track particle emit time */
|
|
3941
3974
|
this.emitTimeBuffer = 0;
|
|
3942
3975
|
}
|
|
3943
3976
|
|
|
@@ -3969,18 +4002,16 @@ class ParticleEmitter extends EngineObject
|
|
|
3969
4002
|
emitParticle()
|
|
3970
4003
|
{
|
|
3971
4004
|
// spawn a particle
|
|
3972
|
-
let pos = this.emitSize
|
|
3973
|
-
|
|
3974
|
-
|
|
3975
|
-
|
|
4005
|
+
let pos = typeof this.emitSize === 'number' ? // check if number was used
|
|
4006
|
+
randInCircle(this.emitSize/2) // circle emitter
|
|
4007
|
+
: vec2(rand(-.5,.5), rand(-.5,.5)) // box emitter
|
|
4008
|
+
.multiply(this.emitSize).rotate(this.angle)
|
|
3976
4009
|
let angle = rand(this.particleConeAngle, -this.particleConeAngle);
|
|
3977
4010
|
if (!this.localSpace)
|
|
3978
4011
|
{
|
|
3979
4012
|
pos = this.pos.add(pos);
|
|
3980
4013
|
angle += this.angle;
|
|
3981
4014
|
}
|
|
3982
|
-
|
|
3983
|
-
const particle = new Particle(pos, this.tileInfo, this.tileSize, angle);
|
|
3984
4015
|
|
|
3985
4016
|
// randomness scales each paremeter by a percentage
|
|
3986
4017
|
const randomness = this.randomness;
|
|
@@ -3996,30 +4027,21 @@ class ParticleEmitter extends EngineObject
|
|
|
3996
4027
|
const colorStart = randColor(this.colorStartA, this.colorStartB, this.randomColorLinear);
|
|
3997
4028
|
const colorEnd = randColor(this.colorEndA, this.colorEndB, this.randomColorLinear);
|
|
3998
4029
|
const velocityAngle = this.localSpace ? coneAngle : this.angle + coneAngle;
|
|
3999
|
-
|
|
4000
|
-
// build particle
|
|
4001
|
-
particle.colorStart
|
|
4002
|
-
particle.
|
|
4003
|
-
particle.
|
|
4004
|
-
particle.
|
|
4005
|
-
particle.
|
|
4006
|
-
particle.
|
|
4007
|
-
particle.
|
|
4008
|
-
particle.
|
|
4009
|
-
particle.
|
|
4010
|
-
particle.
|
|
4011
|
-
particle.
|
|
4012
|
-
|
|
4013
|
-
particle
|
|
4014
|
-
particle.collideTiles = this.collideTiles;
|
|
4015
|
-
particle.additive = this.additive;
|
|
4016
|
-
particle.renderOrder = this.renderOrder;
|
|
4017
|
-
particle.trailScale = this.trailScale;
|
|
4018
|
-
particle.mirror = randInt(2);
|
|
4019
|
-
particle.localSpaceEmitter = this.localSpace && this;
|
|
4020
|
-
|
|
4021
|
-
// setup callbacks for particles
|
|
4022
|
-
particle.destroyCallback = this.particleDestroyCallback;
|
|
4030
|
+
|
|
4031
|
+
// build particle
|
|
4032
|
+
const particle = new Particle(pos, this.tileInfo, angle, colorStart, colorEnd, particleTime, sizeStart, sizeEnd, this.fadeRate, this.additive, this.trailScale, this.localSpace && this, this.particleDestroyCallback);
|
|
4033
|
+
particle.velocity = vec2().setAngle(velocityAngle, speed);
|
|
4034
|
+
particle.fadeRate = this.fadeRate;
|
|
4035
|
+
particle.damping = this.damping;
|
|
4036
|
+
particle.angleDamping = this.angleDamping;
|
|
4037
|
+
particle.elasticity = this.elasticity;
|
|
4038
|
+
particle.friction = this.friction;
|
|
4039
|
+
particle.gravityScale = this.gravityScale;
|
|
4040
|
+
particle.collideTiles = this.collideTiles;
|
|
4041
|
+
particle.renderOrder = this.renderOrder;
|
|
4042
|
+
particle.mirror = !!randInt(2);
|
|
4043
|
+
|
|
4044
|
+
// call particle create callaback
|
|
4023
4045
|
this.particleCreateCallback && this.particleCreateCallback(particle);
|
|
4024
4046
|
|
|
4025
4047
|
// return the newly created particle
|
|
@@ -4038,13 +4060,47 @@ class ParticleEmitter extends EngineObject
|
|
|
4038
4060
|
class Particle extends EngineObject
|
|
4039
4061
|
{
|
|
4040
4062
|
/**
|
|
4041
|
-
* Create a particle with the given
|
|
4042
|
-
* @param {Vector2}
|
|
4043
|
-
* @param {TileInfo} [tileInfo]
|
|
4044
|
-
* @param {Number}
|
|
4063
|
+
* Create a particle with the given shis.colorStart = undefined;ettings
|
|
4064
|
+
* @param {Vector2} position - World space position of the particle
|
|
4065
|
+
* @param {TileInfo} [tileInfo] - Tile info to render particles
|
|
4066
|
+
* @param {Number} [angle] - Angle to rotate the particle
|
|
4067
|
+
* @param {Color} [colorStart] - Color at start of life
|
|
4068
|
+
* @param {Color} [colorEnd] - Color at end of life
|
|
4069
|
+
* @param {Number} [lifeTime] - How long to live for
|
|
4070
|
+
* @param {Number} [sizeStart] - Angle to rotate the particle
|
|
4071
|
+
* @param {Number} [sizeEnd] - Angle to rotate the particle
|
|
4072
|
+
* @param {Number} [fadeRate] - Angle to rotate the particle
|
|
4073
|
+
* @param {Boolean} [additive] - Angle to rotate the particle
|
|
4074
|
+
* @param {Number} [trailScale] - If a trail, how long to make it
|
|
4075
|
+
* @param {ParticleEmitter} [localSpaceEmitter] - Parent emitter if local space
|
|
4076
|
+
* @param {Function} [destroyCallback] - Called when particle dies
|
|
4045
4077
|
*/
|
|
4046
|
-
constructor(
|
|
4047
|
-
|
|
4078
|
+
constructor(position, tileInfo, angle, colorStart, colorEnd, lifeTime, sizeStart, sizeEnd, fadeRate, additive, trailScale, localSpaceEmitter, destroyCallback
|
|
4079
|
+
)
|
|
4080
|
+
{
|
|
4081
|
+
super(position, vec2(), tileInfo, angle);
|
|
4082
|
+
|
|
4083
|
+
/** @property {Color} - Color at start of life */
|
|
4084
|
+
this.colorStart = colorStart;
|
|
4085
|
+
/** @property {Color} - Calculated change in color */
|
|
4086
|
+
this.colorEndDelta = colorEnd.subtract(colorStart);
|
|
4087
|
+
/** @property {Number} - How long to live for */
|
|
4088
|
+
this.lifeTime = lifeTime;
|
|
4089
|
+
/** @property {Number} - Size at start of life */
|
|
4090
|
+
this.sizeStart = sizeStart;
|
|
4091
|
+
/** @property {Number} - Calculated change in size */
|
|
4092
|
+
this.sizeEndDelta = sizeEnd - sizeStart;
|
|
4093
|
+
/** @property {Number} - How quick to fade in/out */
|
|
4094
|
+
this.fadeRate = fadeRate;
|
|
4095
|
+
/** @property {Boolean} - Is it additive */
|
|
4096
|
+
this.additive = additive;
|
|
4097
|
+
/** @property {Number} - If a trail, how long to make it */
|
|
4098
|
+
this.trailScale = trailScale;
|
|
4099
|
+
/** @property {ParticleEmitter} - Parent emitter if local space */
|
|
4100
|
+
this.localSpaceEmitter = localSpaceEmitter;
|
|
4101
|
+
/** @property {Function} - Called when particle dies */
|
|
4102
|
+
this.destroyCallback = destroyCallback;
|
|
4103
|
+
}
|
|
4048
4104
|
|
|
4049
4105
|
/** Render the particle, automatically called each frame, sorted by renderOrder */
|
|
4050
4106
|
render()
|
|
@@ -4062,7 +4118,7 @@ class Particle extends EngineObject
|
|
|
4062
4118
|
(p < fadeRate ? p/fadeRate : p > 1-fadeRate ? (1-p)/fadeRate : 1)); // fade alpha
|
|
4063
4119
|
|
|
4064
4120
|
// draw the particle
|
|
4065
|
-
this.additive && setBlendMode(
|
|
4121
|
+
this.additive && setBlendMode(true);
|
|
4066
4122
|
|
|
4067
4123
|
let pos = this.pos, angle = this.angle;
|
|
4068
4124
|
if (this.localSpaceEmitter)
|
|
@@ -4152,7 +4208,7 @@ class Medal
|
|
|
4152
4208
|
* @param {Number} id - The unique identifier of the medal
|
|
4153
4209
|
* @param {String} name - Name of the medal
|
|
4154
4210
|
* @param {String} [description] - Description of the medal
|
|
4155
|
-
* @param {String} [icon
|
|
4211
|
+
* @param {String} [icon] - Icon for the medal
|
|
4156
4212
|
* @param {String} [src] - Image location for the medal
|
|
4157
4213
|
*/
|
|
4158
4214
|
constructor(id, name, description='', icon='🏆', src)
|
|
@@ -4175,14 +4231,14 @@ class Medal
|
|
|
4175
4231
|
return;
|
|
4176
4232
|
|
|
4177
4233
|
// save the medal
|
|
4178
|
-
ASSERT(medalsSaveName
|
|
4234
|
+
ASSERT(medalsSaveName, 'save name must be set');
|
|
4179
4235
|
localStorage[this.storageKey()] = this.unlocked = 1;
|
|
4180
4236
|
medalsDisplayQueue.push(this);
|
|
4181
4237
|
newgrounds && newgrounds.unlockMedal(this.id);
|
|
4182
4238
|
}
|
|
4183
4239
|
|
|
4184
4240
|
/** Render a medal
|
|
4185
|
-
* @param {Number} [hidePercent
|
|
4241
|
+
* @param {Number} [hidePercent] - How much to slide the medal off screen
|
|
4186
4242
|
*/
|
|
4187
4243
|
render(hidePercent=0)
|
|
4188
4244
|
{
|
|
@@ -4194,25 +4250,25 @@ class Medal
|
|
|
4194
4250
|
// draw containing rect and clip to that region
|
|
4195
4251
|
context.save();
|
|
4196
4252
|
context.beginPath();
|
|
4197
|
-
context.fillStyle =
|
|
4198
|
-
context.strokeStyle =
|
|
4253
|
+
context.fillStyle = rgb(.9,.9,.9).toString();
|
|
4254
|
+
context.strokeStyle = rgb(0,0,0).toString();
|
|
4199
4255
|
context.lineWidth = 3;
|
|
4200
|
-
context.
|
|
4256
|
+
context.rect(x, y, width, medalDisplaySize.y);
|
|
4257
|
+
context.fill();
|
|
4201
4258
|
context.stroke();
|
|
4202
4259
|
context.clip();
|
|
4203
4260
|
|
|
4204
4261
|
// draw the icon and text
|
|
4205
4262
|
this.renderIcon(vec2(x+15+medalDisplayIconSize/2, y+medalDisplaySize.y/2));
|
|
4206
4263
|
const pos = vec2(x+medalDisplayIconSize+30, y+28);
|
|
4207
|
-
drawTextScreen(this.name, pos, 38, new Color(0,0,0), 0,
|
|
4264
|
+
drawTextScreen(this.name, pos, 38, new Color(0,0,0), 0, undefined, 'left');
|
|
4208
4265
|
pos.y += 32;
|
|
4209
|
-
drawTextScreen(this.description, pos, 24, new Color(0,0,0), 0,
|
|
4266
|
+
drawTextScreen(this.description, pos, 24, new Color(0,0,0), 0, undefined, 'left');
|
|
4210
4267
|
context.restore();
|
|
4211
4268
|
}
|
|
4212
4269
|
|
|
4213
4270
|
/** Render the icon for a medal
|
|
4214
|
-
* @param {
|
|
4215
|
-
* @param {Number} y - Screen space Y position
|
|
4271
|
+
* @param {Vector2} pos - Screen space position
|
|
4216
4272
|
* @param {Number} [size=medalDisplayIconSize] - Screen space size
|
|
4217
4273
|
*/
|
|
4218
4274
|
renderIcon(pos, size=medalDisplayIconSize)
|
|
@@ -4240,7 +4296,10 @@ function medalsRender()
|
|
|
4240
4296
|
if (!medalsDisplayTimeLast)
|
|
4241
4297
|
medalsDisplayTimeLast = timeReal;
|
|
4242
4298
|
else if (time > medalDisplayTime)
|
|
4243
|
-
|
|
4299
|
+
{
|
|
4300
|
+
medalsDisplayTimeLast = 0;
|
|
4301
|
+
medalsDisplayQueue.shift();
|
|
4302
|
+
}
|
|
4244
4303
|
else
|
|
4245
4304
|
{
|
|
4246
4305
|
// slide on/off medals
|
|
@@ -4280,8 +4339,8 @@ class Newgrounds
|
|
|
4280
4339
|
* @param {Object} [cryptoJS] - An instance of CryptoJS, if there is a cipher */
|
|
4281
4340
|
constructor(app_id, cipher, cryptoJS)
|
|
4282
4341
|
{
|
|
4283
|
-
ASSERT(!newgrounds && app_id
|
|
4284
|
-
ASSERT(!cipher || cryptoJS
|
|
4342
|
+
ASSERT(!newgrounds && app_id>0, 'there can only be one newgrounds object');
|
|
4343
|
+
ASSERT(!cipher || cryptoJS, 'must provide cryptojs if there is a cipher');
|
|
4285
4344
|
|
|
4286
4345
|
this.app_id = app_id;
|
|
4287
4346
|
this.cipher = cipher;
|
|
@@ -4324,39 +4383,39 @@ class Newgrounds
|
|
|
4324
4383
|
debugMedals && console.log(this.scoreboards);
|
|
4325
4384
|
|
|
4326
4385
|
const keepAliveMS = 5 * 60 * 1e3;
|
|
4327
|
-
setInterval(()=>this.call('Gateway.ping', 0,
|
|
4386
|
+
setInterval(()=>this.call('Gateway.ping', 0, true), keepAliveMS);
|
|
4328
4387
|
}
|
|
4329
4388
|
|
|
4330
4389
|
/** Send message to unlock a medal by id
|
|
4331
4390
|
* @param {Number} id - The medal id */
|
|
4332
|
-
unlockMedal(id) { return this.call('Medal.unlock', {'id':id},
|
|
4391
|
+
unlockMedal(id) { return this.call('Medal.unlock', {'id':id}, true); }
|
|
4333
4392
|
|
|
4334
4393
|
/** Send message to post score
|
|
4335
4394
|
* @param {Number} id - The scoreboard id
|
|
4336
4395
|
* @param {Number} value - The score value */
|
|
4337
|
-
postScore(id, value) { return this.call('ScoreBoard.postScore', {'id':id, 'value':value},
|
|
4396
|
+
postScore(id, value) { return this.call('ScoreBoard.postScore', {'id':id, 'value':value}, true); }
|
|
4338
4397
|
|
|
4339
4398
|
/** Get scores from a scoreboard
|
|
4340
|
-
* @param {Number} id
|
|
4341
|
-
* @param {String} [user
|
|
4342
|
-
* @param {Number} [social
|
|
4343
|
-
* @param {Number} [skip
|
|
4344
|
-
* @param {Number} [limit
|
|
4345
|
-
* @return {Object}
|
|
4399
|
+
* @param {Number} id - The scoreboard id
|
|
4400
|
+
* @param {String} [user] - A user's id or name
|
|
4401
|
+
* @param {Number} [social] - If true, only social scores will be loaded
|
|
4402
|
+
* @param {Number} [skip] - Number of scores to skip before start
|
|
4403
|
+
* @param {Number} [limit] - Number of scores to include in the list
|
|
4404
|
+
* @return {Object} - The response JSON object
|
|
4346
4405
|
*/
|
|
4347
|
-
getScores(id, user
|
|
4406
|
+
getScores(id, user, social=0, skip=0, limit=10)
|
|
4348
4407
|
{ return this.call('ScoreBoard.getScores', {'id':id, 'user':user, 'social':social, 'skip':skip, 'limit':limit}); }
|
|
4349
4408
|
|
|
4350
4409
|
/** Send message to log a view */
|
|
4351
|
-
logView() { return this.call('App.logView', {'host':this.host},
|
|
4410
|
+
logView() { return this.call('App.logView', {'host':this.host}, true); }
|
|
4352
4411
|
|
|
4353
4412
|
/** Send a message to call a component of the Newgrounds API
|
|
4354
|
-
* @param {String} component
|
|
4355
|
-
* @param {Object} [parameters
|
|
4356
|
-
* @param {Boolean} [async
|
|
4357
|
-
* @return {Object}
|
|
4413
|
+
* @param {String} component - Name of the component
|
|
4414
|
+
* @param {Object} [parameters] - Parameters to use for call
|
|
4415
|
+
* @param {Boolean} [async] - If true, don't wait for response before continuing
|
|
4416
|
+
* @return {Object} - The response JSON object
|
|
4358
4417
|
*/
|
|
4359
|
-
call(component, parameters
|
|
4418
|
+
call(component, parameters, async=false)
|
|
4360
4419
|
{
|
|
4361
4420
|
const call = {'component':component, 'parameters':parameters};
|
|
4362
4421
|
if (this.cipher)
|
|
@@ -4391,7 +4450,7 @@ class Newgrounds
|
|
|
4391
4450
|
return xmlHttp.responseText && JSON.parse(xmlHttp.responseText);
|
|
4392
4451
|
}
|
|
4393
4452
|
}
|
|
4394
|
-
/**
|
|
4453
|
+
/**
|
|
4395
4454
|
* LittleJS WebGL Interface
|
|
4396
4455
|
* - All webgl used by the engine is wrapped up here
|
|
4397
4456
|
* - For normal stuff you won't need to see or call anything in this file
|
|
@@ -4410,13 +4469,13 @@ class Newgrounds
|
|
|
4410
4469
|
* @memberof WebGL */
|
|
4411
4470
|
let glCanvas;
|
|
4412
4471
|
|
|
4413
|
-
/** 2d context for glCanvas
|
|
4414
|
-
* @type {
|
|
4472
|
+
/** 2d context for glCanvas
|
|
4473
|
+
* @type {WebGL2RenderingContext}
|
|
4415
4474
|
* @memberof WebGL */
|
|
4416
4475
|
let glContext;
|
|
4417
4476
|
|
|
4418
4477
|
// WebGL internal variables not exposed to documentation
|
|
4419
|
-
let
|
|
4478
|
+
let glShader, glActiveTexture, glArrayBuffer, glGeometryBuffer, glPositionData, glColorData, glInstanceCount, glAdditive, glBatchAdditive;
|
|
4420
4479
|
|
|
4421
4480
|
///////////////////////////////////////////////////////////////////////////////
|
|
4422
4481
|
|
|
@@ -4432,73 +4491,89 @@ function glInit()
|
|
|
4432
4491
|
|
|
4433
4492
|
// setup vertex and fragment shaders
|
|
4434
4493
|
glShader = glCreateProgram(
|
|
4435
|
-
'#version 300 es\n' +
|
|
4436
|
-
'precision highp float;'+
|
|
4437
|
-
'uniform mat4 m;'+
|
|
4438
|
-
'in
|
|
4439
|
-
'
|
|
4440
|
-
'
|
|
4441
|
-
'
|
|
4442
|
-
'
|
|
4443
|
-
'
|
|
4494
|
+
'#version 300 es\n' + // specify GLSL ES version
|
|
4495
|
+
'precision highp float;'+ // use highp for better accuracy
|
|
4496
|
+
'uniform mat4 m;'+ // transform matrix
|
|
4497
|
+
'in vec2 g;'+ // geometry
|
|
4498
|
+
'in vec4 p,u,c,a;'+ // position/size, uvs, color, additiveColor
|
|
4499
|
+
'in float r;'+ // rotation
|
|
4500
|
+
'out vec2 v;'+ // return uv, color, additiveColor
|
|
4501
|
+
'out vec4 d,e;'+ // return uv, color, additiveColor
|
|
4502
|
+
'void main(){'+ // shader entry point
|
|
4503
|
+
'vec2 s=(g-.5)*p.zw;'+ // get size offset
|
|
4504
|
+
'gl_Position=m*vec4(p.xy+s*cos(r)-vec2(-s.y,s)*sin(r),1,1);'+ // transform position
|
|
4505
|
+
'v=mix(u.xw,u.zy,g);'+ // pass uv to fragment shader
|
|
4506
|
+
'd=c;e=a;'+ // pass colors to fragment shader
|
|
4507
|
+
'}' // end of shader
|
|
4444
4508
|
,
|
|
4445
|
-
'#version 300 es\n' +
|
|
4446
|
-
'precision highp float;'+
|
|
4447
|
-
'in
|
|
4448
|
-
'
|
|
4449
|
-
'
|
|
4450
|
-
'
|
|
4451
|
-
'
|
|
4452
|
-
'
|
|
4509
|
+
'#version 300 es\n' + // specify GLSL ES version
|
|
4510
|
+
'precision highp float;'+ // use highp for better accuracy
|
|
4511
|
+
'in vec2 v;'+ // uv
|
|
4512
|
+
'in vec4 d,e;'+ // color, additiveColor
|
|
4513
|
+
'uniform sampler2D s;'+ // texture
|
|
4514
|
+
'out vec4 c;'+ // out color
|
|
4515
|
+
'void main(){'+ // shader entry point
|
|
4516
|
+
'c=texture(s,v)*d+e;'+ // modulate texture by color plus additive
|
|
4517
|
+
'}' // end of shader
|
|
4453
4518
|
);
|
|
4454
4519
|
|
|
4455
4520
|
// init buffers
|
|
4456
|
-
|
|
4457
|
-
glPositionData = new Float32Array(
|
|
4458
|
-
glColorData = new Uint32Array(
|
|
4521
|
+
const glInstanceData = new ArrayBuffer(gl_INSTANCE_BUFFER_SIZE);
|
|
4522
|
+
glPositionData = new Float32Array(glInstanceData);
|
|
4523
|
+
glColorData = new Uint32Array(glInstanceData);
|
|
4459
4524
|
glArrayBuffer = glContext.createBuffer();
|
|
4460
|
-
|
|
4525
|
+
glGeometryBuffer = glContext.createBuffer();
|
|
4526
|
+
|
|
4527
|
+
// create the geometry buffer, triangle strip square
|
|
4528
|
+
const geometry = new Float32Array([glInstanceCount=0,0,1,0,0,1,1,1]);
|
|
4529
|
+
glContext.bindBuffer(gl_ARRAY_BUFFER, glGeometryBuffer);
|
|
4530
|
+
glContext.bufferData(gl_ARRAY_BUFFER, geometry, gl_STATIC_DRAW);
|
|
4461
4531
|
}
|
|
4462
4532
|
|
|
4463
4533
|
// Setup render each frame, called automatically by engine
|
|
4464
4534
|
function glPreRender()
|
|
4465
4535
|
{
|
|
4466
4536
|
// clear and set to same size as main canvas
|
|
4467
|
-
glContext.viewport(0, 0, glCanvas.width
|
|
4537
|
+
glContext.viewport(0, 0, glCanvas.width=mainCanvas.width, glCanvas.height=mainCanvas.height);
|
|
4468
4538
|
glContext.clear(gl_COLOR_BUFFER_BIT);
|
|
4469
4539
|
|
|
4470
4540
|
// set up the shader
|
|
4471
4541
|
glContext.useProgram(glShader);
|
|
4472
4542
|
glContext.activeTexture(gl_TEXTURE0);
|
|
4473
4543
|
glContext.bindTexture(gl_TEXTURE_2D, glActiveTexture = textureInfos[0].glTexture);
|
|
4474
|
-
|
|
4475
|
-
glContext.bufferData(gl_ARRAY_BUFFER, gl_VERTEX_BUFFER_SIZE, gl_DYNAMIC_DRAW);
|
|
4476
|
-
glAdditive = 0;
|
|
4477
|
-
|
|
4544
|
+
|
|
4478
4545
|
// set vertex attributes
|
|
4479
|
-
let offset = 0;
|
|
4480
|
-
|
|
4546
|
+
let offset = glAdditive = glBatchAdditive = 0;
|
|
4547
|
+
let initVertexAttribArray = (name, type, typeSize, size)=>
|
|
4481
4548
|
{
|
|
4482
4549
|
const location = glContext.getAttribLocation(glShader, name);
|
|
4550
|
+
const stride = typeSize && gl_INSTANCE_BYTE_STRIDE; // only if not geometry
|
|
4551
|
+
const divisor = typeSize && 1; // only if not geometry
|
|
4552
|
+
const normalize = typeSize==1; // only if color
|
|
4483
4553
|
glContext.enableVertexAttribArray(location);
|
|
4484
|
-
glContext.vertexAttribPointer(location, size, type, normalize,
|
|
4554
|
+
glContext.vertexAttribPointer(location, size, type, normalize, stride, offset);
|
|
4555
|
+
glContext.vertexAttribDivisor(location, divisor);
|
|
4485
4556
|
offset += size*typeSize;
|
|
4486
4557
|
}
|
|
4487
|
-
|
|
4488
|
-
initVertexAttribArray('
|
|
4489
|
-
|
|
4558
|
+
glContext.bindBuffer(gl_ARRAY_BUFFER, glGeometryBuffer);
|
|
4559
|
+
initVertexAttribArray('g', gl_FLOAT, 0, 2); // geometry
|
|
4560
|
+
glContext.bindBuffer(gl_ARRAY_BUFFER, glArrayBuffer);
|
|
4561
|
+
glContext.bufferData(gl_ARRAY_BUFFER, gl_INSTANCE_BUFFER_SIZE, gl_DYNAMIC_DRAW);
|
|
4562
|
+
initVertexAttribArray('p', gl_FLOAT, 4, 4); // position & size
|
|
4563
|
+
initVertexAttribArray('u', gl_FLOAT, 4, 4); // texture coords
|
|
4564
|
+
initVertexAttribArray('c', gl_UNSIGNED_BYTE, 1, 4); // color
|
|
4565
|
+
initVertexAttribArray('a', gl_UNSIGNED_BYTE, 1, 4); // additiveColor
|
|
4566
|
+
initVertexAttribArray('r', gl_FLOAT, 4, 1); // rotation
|
|
4490
4567
|
|
|
4491
4568
|
// build the transform matrix
|
|
4492
|
-
const
|
|
4493
|
-
const
|
|
4494
|
-
|
|
4495
|
-
const cy = -1 - sy*cameraPos.y;
|
|
4496
|
-
glContext.uniformMatrix4fv(glContext.getUniformLocation(glShader, 'm'), 0,
|
|
4569
|
+
const s = vec2(2*cameraScale).divide(mainCanvasSize);
|
|
4570
|
+
const p = vec2(-1).subtract(cameraPos.multiply(s));
|
|
4571
|
+
glContext.uniformMatrix4fv(glContext.getUniformLocation(glShader, 'm'), false,
|
|
4497
4572
|
new Float32Array([
|
|
4498
|
-
|
|
4499
|
-
|
|
4500
|
-
|
|
4501
|
-
|
|
4573
|
+
s.x, 0, 0, 0,
|
|
4574
|
+
0, s.y, 0, 0,
|
|
4575
|
+
1, 1, 1, 1,
|
|
4576
|
+
p.x, p.y, 0, 0
|
|
4502
4577
|
])
|
|
4503
4578
|
);
|
|
4504
4579
|
}
|
|
@@ -4519,7 +4594,7 @@ function glSetTexture(texture)
|
|
|
4519
4594
|
|
|
4520
4595
|
/** Compile WebGL shader of the given type, will throw errors if in debug mode
|
|
4521
4596
|
* @param {String} source
|
|
4522
|
-
* @param
|
|
4597
|
+
* @param {Number} type
|
|
4523
4598
|
* @return {WebGLShader}
|
|
4524
4599
|
* @memberof WebGL */
|
|
4525
4600
|
function glCompileShader(source, type)
|
|
@@ -4536,8 +4611,8 @@ function glCompileShader(source, type)
|
|
|
4536
4611
|
}
|
|
4537
4612
|
|
|
4538
4613
|
/** Create WebGL program with given shaders
|
|
4539
|
-
* @param {
|
|
4540
|
-
* @param {
|
|
4614
|
+
* @param {String} vsSource
|
|
4615
|
+
* @param {String} fsSource
|
|
4541
4616
|
* @return {WebGLProgram}
|
|
4542
4617
|
* @memberof WebGL */
|
|
4543
4618
|
function glCreateProgram(vsSource, fsSource)
|
|
@@ -4555,7 +4630,7 @@ function glCreateProgram(vsSource, fsSource)
|
|
|
4555
4630
|
}
|
|
4556
4631
|
|
|
4557
4632
|
/** Create WebGL texture from an image and init the texture settings
|
|
4558
|
-
* @param {
|
|
4633
|
+
* @param {HTMLImageElement} image
|
|
4559
4634
|
* @return {WebGLTexture}
|
|
4560
4635
|
* @memberof WebGL */
|
|
4561
4636
|
function glCreateTexture(image)
|
|
@@ -4565,7 +4640,7 @@ function glCreateTexture(image)
|
|
|
4565
4640
|
glContext.bindTexture(gl_TEXTURE_2D, texture);
|
|
4566
4641
|
if (image)
|
|
4567
4642
|
glContext.texImage2D(gl_TEXTURE_2D, 0, gl_RGBA, gl_RGBA, gl_UNSIGNED_BYTE, image);
|
|
4568
|
-
|
|
4643
|
+
|
|
4569
4644
|
// use point filtering for pixelated rendering
|
|
4570
4645
|
const filter = canvasPixelated ? gl_NEAREST : gl_LINEAR;
|
|
4571
4646
|
glContext.texParameteri(gl_TEXTURE_2D, gl_TEXTURE_MIN_FILTER, filter);
|
|
@@ -4580,29 +4655,31 @@ function glCreateTexture(image)
|
|
|
4580
4655
|
* @memberof WebGL */
|
|
4581
4656
|
function glFlush()
|
|
4582
4657
|
{
|
|
4583
|
-
if (!
|
|
4658
|
+
if (!glInstanceCount) return;
|
|
4584
4659
|
|
|
4585
4660
|
const destBlend = glBatchAdditive ? gl_ONE : gl_ONE_MINUS_SRC_ALPHA;
|
|
4586
4661
|
glContext.blendFuncSeparate(gl_SRC_ALPHA, destBlend, gl_ONE, destBlend);
|
|
4587
4662
|
glContext.enable(gl_BLEND);
|
|
4588
4663
|
|
|
4589
4664
|
// draw all the sprites in the batch and reset the buffer
|
|
4590
|
-
glContext.bufferSubData(gl_ARRAY_BUFFER, 0,
|
|
4591
|
-
glContext.
|
|
4592
|
-
|
|
4665
|
+
glContext.bufferSubData(gl_ARRAY_BUFFER, 0, glPositionData);
|
|
4666
|
+
glContext.drawArraysInstanced(gl_TRIANGLE_STRIP, 0, 4, glInstanceCount);
|
|
4667
|
+
if (showWatermark)
|
|
4668
|
+
drawCount += glInstanceCount;
|
|
4669
|
+
glInstanceCount = 0;
|
|
4593
4670
|
glBatchAdditive = glAdditive;
|
|
4594
4671
|
}
|
|
4595
4672
|
|
|
4596
4673
|
/** Draw any sprites still in the buffer, copy to main canvas and clear
|
|
4597
4674
|
* @param {CanvasRenderingContext2D} context
|
|
4598
|
-
* @param {Boolean} [forceDraw
|
|
4675
|
+
* @param {Boolean} [forceDraw]
|
|
4599
4676
|
* @memberof WebGL */
|
|
4600
|
-
function glCopyToContext(context, forceDraw)
|
|
4677
|
+
function glCopyToContext(context, forceDraw=false)
|
|
4601
4678
|
{
|
|
4602
|
-
if (!
|
|
4603
|
-
|
|
4679
|
+
if (!glInstanceCount && !forceDraw) return;
|
|
4680
|
+
|
|
4604
4681
|
glFlush();
|
|
4605
|
-
|
|
4682
|
+
|
|
4606
4683
|
// do not draw in overlay mode because the canvas is visible
|
|
4607
4684
|
if (!glOverlay || forceDraw)
|
|
4608
4685
|
context.drawImage(glCanvas, 0, 0);
|
|
@@ -4623,61 +4700,25 @@ function glCopyToContext(context, forceDraw)
|
|
|
4623
4700
|
* @memberof WebGL */
|
|
4624
4701
|
function glDraw(x, y, sizeX, sizeY, angle, uv0X, uv0Y, uv1X, uv1Y, rgba, rgbaAdditive=0)
|
|
4625
4702
|
{
|
|
4626
|
-
|
|
4627
|
-
const vertCount = 6;
|
|
4628
|
-
if (glBatchCount >= gl_MAX_BATCH-vertCount || glBatchAdditive != glAdditive)
|
|
4629
|
-
glFlush();
|
|
4630
|
-
|
|
4631
|
-
// prepare to create the verts from size and angle
|
|
4632
|
-
const c = Math.cos(angle)/2, s = Math.sin(angle)/2;
|
|
4633
|
-
const cx = c*sizeX, cy = c*sizeY, sx = s*sizeX, sy = s*sizeY;
|
|
4634
|
-
const positionData =
|
|
4635
|
-
[
|
|
4636
|
-
x-cx+sy, y+cy+sx, uv0X, uv0Y,
|
|
4637
|
-
x-cx-sy, y-cy+sx, uv0X, uv1Y,
|
|
4638
|
-
x+cx+sy, y+cy-sx, uv1X, uv0Y,
|
|
4639
|
-
x+cx-sy, y-cy-sx, uv1X, uv1Y,
|
|
4640
|
-
];
|
|
4641
|
-
|
|
4642
|
-
// setup 2 triangle strip quad
|
|
4643
|
-
for(let i = vertCount, offset = glBatchCount * gl_INDICIES_PER_VERT; i--;)
|
|
4644
|
-
{
|
|
4645
|
-
const j = clamp(i-1, 0, 3)*4; // degenerate tri at ends
|
|
4646
|
-
glPositionData[offset++] = positionData[j+0];
|
|
4647
|
-
glPositionData[offset++] = positionData[j+1];
|
|
4648
|
-
glPositionData[offset++] = positionData[j+2];
|
|
4649
|
-
glPositionData[offset++] = positionData[j+3];
|
|
4650
|
-
glColorData[offset++] = rgba;
|
|
4651
|
-
glColorData[offset++] = rgbaAdditive;
|
|
4652
|
-
}
|
|
4653
|
-
glBatchCount += vertCount;
|
|
4654
|
-
}
|
|
4703
|
+
ASSERT(typeof rgba == 'number' && typeof rgbaAdditive == 'number', 'invalid color');
|
|
4655
4704
|
|
|
4656
|
-
/** Add a convex polygon to the gl draw list
|
|
4657
|
-
* @param {Array} points - Array of Vector2 points
|
|
4658
|
-
* @param {Number} rgba - Color of the polygon
|
|
4659
|
-
* @memberof WebGL */
|
|
4660
|
-
function glDrawPoints(points, rgba)
|
|
4661
|
-
{
|
|
4662
4705
|
// flush if there is not enough room or if different blend mode
|
|
4663
|
-
|
|
4664
|
-
if (glBatchCount >= gl_MAX_BATCH-vertCount || glBatchAdditive != glAdditive)
|
|
4706
|
+
if (glInstanceCount >= gl_MAX_INSTANCES-1 || glBatchAdditive != glAdditive)
|
|
4665
4707
|
glFlush();
|
|
4666
|
-
|
|
4667
|
-
|
|
4668
|
-
|
|
4669
|
-
|
|
4670
|
-
|
|
4671
|
-
|
|
4672
|
-
|
|
4673
|
-
|
|
4674
|
-
|
|
4675
|
-
|
|
4676
|
-
|
|
4677
|
-
|
|
4678
|
-
|
|
4679
|
-
|
|
4680
|
-
glBatchCount += vertCount;
|
|
4708
|
+
|
|
4709
|
+
let offset = glInstanceCount * gl_INDICIES_PER_INSTANCE;
|
|
4710
|
+
glPositionData[offset++] = x;
|
|
4711
|
+
glPositionData[offset++] = y;
|
|
4712
|
+
glPositionData[offset++] = sizeX;
|
|
4713
|
+
glPositionData[offset++] = sizeY;
|
|
4714
|
+
glPositionData[offset++] = uv0X;
|
|
4715
|
+
glPositionData[offset++] = uv0Y;
|
|
4716
|
+
glPositionData[offset++] = uv1X;
|
|
4717
|
+
glPositionData[offset++] = uv1Y;
|
|
4718
|
+
glColorData[offset++] = rgba;
|
|
4719
|
+
glColorData[offset++] = rgbaAdditive;
|
|
4720
|
+
glPositionData[offset++] = angle;
|
|
4721
|
+
glInstanceCount++;
|
|
4681
4722
|
}
|
|
4682
4723
|
|
|
4683
4724
|
///////////////////////////////////////////////////////////////////////////////
|
|
@@ -4689,9 +4730,9 @@ let glPostShader, glPostArrayBuffer, glPostTexture, glPostIncludeOverlay;
|
|
|
4689
4730
|
* @param {String} shaderCode
|
|
4690
4731
|
* @param {Boolean} includeOverlay
|
|
4691
4732
|
* @memberof WebGL */
|
|
4692
|
-
function glInitPostProcess(shaderCode, includeOverlay)
|
|
4733
|
+
function glInitPostProcess(shaderCode, includeOverlay=false)
|
|
4693
4734
|
{
|
|
4694
|
-
ASSERT(!glPostShader
|
|
4735
|
+
ASSERT(!glPostShader, 'can only have 1 post effects shader');
|
|
4695
4736
|
|
|
4696
4737
|
if (!shaderCode) // default shader pass through
|
|
4697
4738
|
shaderCode = 'void mainImage(out vec4 c,vec2 p){c=texture(iChannel0,p/iResolution.xy);}';
|
|
@@ -4702,14 +4743,14 @@ function glInitPostProcess(shaderCode, includeOverlay)
|
|
|
4702
4743
|
'precision highp float;'+ // use highp for better accuracy
|
|
4703
4744
|
'in vec2 p;'+ // position
|
|
4704
4745
|
'void main(){'+ // shader entry point
|
|
4705
|
-
'gl_Position=vec4(p
|
|
4746
|
+
'gl_Position=vec4(p+p-1.,1,1);'+ // set position
|
|
4706
4747
|
'}' // end of shader
|
|
4707
4748
|
,
|
|
4708
4749
|
'#version 300 es\n' + // specify GLSL ES version
|
|
4709
4750
|
'precision highp float;'+ // use highp for better accuracy
|
|
4710
4751
|
'uniform sampler2D iChannel0;'+ // input texture
|
|
4711
4752
|
'uniform vec3 iResolution;'+ // size of output texture
|
|
4712
|
-
'uniform float iTime;'+ // time
|
|
4753
|
+
'uniform float iTime;'+ // time
|
|
4713
4754
|
'out vec4 c;'+ // out color
|
|
4714
4755
|
'\n' + shaderCode + '\n'+ // insert custom shader code
|
|
4715
4756
|
'void main(){'+ // shader entry point
|
|
@@ -4720,11 +4761,13 @@ function glInitPostProcess(shaderCode, includeOverlay)
|
|
|
4720
4761
|
|
|
4721
4762
|
// create buffer and texture
|
|
4722
4763
|
glPostArrayBuffer = glContext.createBuffer();
|
|
4723
|
-
glPostTexture = glCreateTexture();
|
|
4764
|
+
glPostTexture = glCreateTexture(undefined);
|
|
4724
4765
|
glPostIncludeOverlay = includeOverlay;
|
|
4725
4766
|
|
|
4726
4767
|
// hide the original 2d canvas
|
|
4727
4768
|
mainCanvas.style.visibility = 'hidden';
|
|
4769
|
+
if (glPostIncludeOverlay)
|
|
4770
|
+
overlayCanvas.style.visibility = 'hidden';
|
|
4728
4771
|
}
|
|
4729
4772
|
|
|
4730
4773
|
// Render the post processing shader, called automatically by the engine
|
|
@@ -4745,21 +4788,14 @@ function glRenderPostProcess()
|
|
|
4745
4788
|
glContext.viewport(0, 0, glCanvas.width = mainCanvas.width, glCanvas.height = mainCanvas.height);
|
|
4746
4789
|
}
|
|
4747
4790
|
|
|
4748
|
-
|
|
4749
|
-
|
|
4750
|
-
// copy overlay canvas so it will be included in post processing
|
|
4751
|
-
mainContext.drawImage(overlayCanvas, 0, 0);
|
|
4752
|
-
|
|
4753
|
-
// clear overlay canvas
|
|
4754
|
-
overlayCanvas.width = mainCanvas.width;
|
|
4755
|
-
}
|
|
4791
|
+
// copy overlay canvas so it will be included in post processing
|
|
4792
|
+
glPostIncludeOverlay && mainContext.drawImage(overlayCanvas, 0, 0);
|
|
4756
4793
|
|
|
4757
4794
|
// setup shader program to draw one triangle
|
|
4758
4795
|
glContext.useProgram(glPostShader);
|
|
4796
|
+
glContext.bindBuffer(gl_ARRAY_BUFFER, glGeometryBuffer);
|
|
4797
|
+
glContext.pixelStorei(gl_UNPACK_FLIP_Y_WEBGL, 1);
|
|
4759
4798
|
glContext.disable(gl_BLEND);
|
|
4760
|
-
glContext.bindBuffer(gl_ARRAY_BUFFER, glPostArrayBuffer);
|
|
4761
|
-
glContext.bufferData(gl_ARRAY_BUFFER, new Float32Array([-3,1,1,-3,1,1]), gl_STATIC_DRAW);
|
|
4762
|
-
glContext.pixelStorei(gl_UNPACK_FLIP_Y_WEBGL, true);
|
|
4763
4799
|
|
|
4764
4800
|
// set textures, pass in the 2d canvas and gl canvas in separate texture channels
|
|
4765
4801
|
glContext.activeTexture(gl_TEXTURE0);
|
|
@@ -4770,19 +4806,19 @@ function glRenderPostProcess()
|
|
|
4770
4806
|
const vertexByteStride = 8;
|
|
4771
4807
|
const pLocation = glContext.getAttribLocation(glPostShader, 'p');
|
|
4772
4808
|
glContext.enableVertexAttribArray(pLocation);
|
|
4773
|
-
glContext.vertexAttribPointer(pLocation, 2, gl_FLOAT,
|
|
4809
|
+
glContext.vertexAttribPointer(pLocation, 2, gl_FLOAT, false, vertexByteStride, 0);
|
|
4774
4810
|
|
|
4775
4811
|
// set uniforms and draw
|
|
4776
4812
|
const uniformLocation = (name)=>glContext.getUniformLocation(glPostShader, name);
|
|
4777
4813
|
glContext.uniform1i(uniformLocation('iChannel0'), 0);
|
|
4778
4814
|
glContext.uniform1f(uniformLocation('iTime'), time);
|
|
4779
4815
|
glContext.uniform3f(uniformLocation('iResolution'), mainCanvas.width, mainCanvas.height, 1);
|
|
4780
|
-
glContext.drawArrays(gl_TRIANGLE_STRIP, 0,
|
|
4816
|
+
glContext.drawArrays(gl_TRIANGLE_STRIP, 0, 4);
|
|
4781
4817
|
}
|
|
4782
4818
|
|
|
4783
4819
|
///////////////////////////////////////////////////////////////////////////////
|
|
4784
4820
|
// store gl constants as integers so their name doesn't use space in minifed
|
|
4785
|
-
const
|
|
4821
|
+
const
|
|
4786
4822
|
gl_ONE = 1,
|
|
4787
4823
|
gl_TRIANGLE_STRIP = 5,
|
|
4788
4824
|
gl_SRC_ALPHA = 770,
|
|
@@ -4804,17 +4840,17 @@ gl_TEXTURE0 = 33984,
|
|
|
4804
4840
|
gl_ARRAY_BUFFER = 34962,
|
|
4805
4841
|
gl_STATIC_DRAW = 35044,
|
|
4806
4842
|
gl_DYNAMIC_DRAW = 35048,
|
|
4807
|
-
gl_FRAGMENT_SHADER = 35632,
|
|
4843
|
+
gl_FRAGMENT_SHADER = 35632,
|
|
4808
4844
|
gl_VERTEX_SHADER = 35633,
|
|
4809
4845
|
gl_COMPILE_STATUS = 35713,
|
|
4810
4846
|
gl_LINK_STATUS = 35714,
|
|
4811
4847
|
gl_UNPACK_FLIP_Y_WEBGL = 37440,
|
|
4812
4848
|
|
|
4813
4849
|
// constants for batch rendering
|
|
4814
|
-
|
|
4815
|
-
|
|
4816
|
-
|
|
4817
|
-
|
|
4850
|
+
gl_INDICIES_PER_INSTANCE = 11,
|
|
4851
|
+
gl_MAX_INSTANCES = 1e4,
|
|
4852
|
+
gl_INSTANCE_BYTE_STRIDE = gl_INDICIES_PER_INSTANCE * 4, // 11 * 4
|
|
4853
|
+
gl_INSTANCE_BUFFER_SIZE = gl_MAX_INSTANCES * gl_INSTANCE_BYTE_STRIDE;
|
|
4818
4854
|
/**
|
|
4819
4855
|
* LittleJS - The Tiny JavaScript Game Engine That Can!
|
|
4820
4856
|
* MIT License - Copyright 2021 Frank Force
|
|
@@ -4847,7 +4883,7 @@ const engineName = 'LittleJS';
|
|
|
4847
4883
|
* @type {String}
|
|
4848
4884
|
* @default
|
|
4849
4885
|
* @memberof Engine */
|
|
4850
|
-
const engineVersion = '1.
|
|
4886
|
+
const engineVersion = '1.9.1';
|
|
4851
4887
|
|
|
4852
4888
|
/** Frames per second to update objects
|
|
4853
4889
|
* @type {Number}
|
|
@@ -4888,14 +4924,14 @@ let timeReal = 0;
|
|
|
4888
4924
|
|
|
4889
4925
|
/** Is the game paused? Causes time and objects to not be updated
|
|
4890
4926
|
* @type {Boolean}
|
|
4891
|
-
* @default
|
|
4927
|
+
* @default false
|
|
4892
4928
|
* @memberof Engine */
|
|
4893
|
-
let paused =
|
|
4929
|
+
let paused = false;
|
|
4894
4930
|
|
|
4895
4931
|
/** Set if game is paused
|
|
4896
|
-
* @param {Boolean}
|
|
4932
|
+
* @param {Boolean} isPaused
|
|
4897
4933
|
* @memberof Engine */
|
|
4898
|
-
function setPaused(
|
|
4934
|
+
function setPaused(isPaused) { paused = isPaused; }
|
|
4899
4935
|
|
|
4900
4936
|
// Frame time tracking
|
|
4901
4937
|
let frameTimeLastMS = 0, frameTimeBufferMS = 0, averageFPS = 0;
|
|
@@ -4912,7 +4948,7 @@ let frameTimeLastMS = 0, frameTimeBufferMS = 0, averageFPS = 0;
|
|
|
4912
4948
|
* @memberof Engine */
|
|
4913
4949
|
function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRenderPost, imageSources=['tiles.png'])
|
|
4914
4950
|
{
|
|
4915
|
-
ASSERT(Array.isArray(imageSources)
|
|
4951
|
+
ASSERT(Array.isArray(imageSources), 'pass in images as array');
|
|
4916
4952
|
|
|
4917
4953
|
// internal update loop for engine
|
|
4918
4954
|
function engineUpdate(frameTimeMS=0)
|
|
@@ -4922,12 +4958,12 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
4922
4958
|
frameTimeLastMS = frameTimeMS;
|
|
4923
4959
|
if (debug || showWatermark)
|
|
4924
4960
|
averageFPS = lerp(.05, averageFPS, 1e3/(frameTimeDeltaMS||1));
|
|
4925
|
-
const debugSpeedUp = debug && keyIsDown(
|
|
4926
|
-
const debugSpeedDown = debug && keyIsDown(
|
|
4961
|
+
const debugSpeedUp = debug && keyIsDown('Equal'); // +
|
|
4962
|
+
const debugSpeedDown = debug && keyIsDown('Minus'); // -
|
|
4927
4963
|
if (debug) // +/- to speed/slow time
|
|
4928
4964
|
frameTimeDeltaMS *= debugSpeedUp ? 5 : debugSpeedDown ? .2 : 1;
|
|
4929
4965
|
timeReal += frameTimeDeltaMS / 1e3;
|
|
4930
|
-
frameTimeBufferMS +=
|
|
4966
|
+
frameTimeBufferMS += paused ? 0 : frameTimeDeltaMS;
|
|
4931
4967
|
if (!debugSpeedUp)
|
|
4932
4968
|
frameTimeBufferMS = min(frameTimeBufferMS, 50); // clamp incase of slow framerate
|
|
4933
4969
|
|
|
@@ -5037,7 +5073,7 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
5037
5073
|
'user-select:none;' + // prevent mobile hold to select
|
|
5038
5074
|
'-webkit-user-select:none;' + // compatibility for ios
|
|
5039
5075
|
'-webkit-touch-callout:none'; // compatibility for ios
|
|
5040
|
-
document.body.style = styleBody;
|
|
5076
|
+
document.body.style.cssText = styleBody;
|
|
5041
5077
|
document.body.appendChild(mainCanvas = document.createElement('canvas'));
|
|
5042
5078
|
mainContext = mainCanvas.getContext('2d');
|
|
5043
5079
|
|
|
@@ -5050,10 +5086,9 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
5050
5086
|
overlayContext = overlayCanvas.getContext('2d');
|
|
5051
5087
|
|
|
5052
5088
|
// set canvas style
|
|
5053
|
-
const styleCanvas =
|
|
5054
|
-
'position:absolute;' + // position
|
|
5089
|
+
const styleCanvas = 'position:absolute;' + // position
|
|
5055
5090
|
'top:50%;left:50%;transform:translate(-50%,-50%)'; // center
|
|
5056
|
-
(glCanvas||mainCanvas).style = mainCanvas.style = overlayCanvas.style = styleCanvas;
|
|
5091
|
+
(glCanvas||mainCanvas).style.cssText = mainCanvas.style.cssText = overlayCanvas.style.cssText = styleCanvas;
|
|
5057
5092
|
|
|
5058
5093
|
// create promises for loading images
|
|
5059
5094
|
const promises = imageSources.map((src, textureIndex)=>
|
|
@@ -5073,13 +5108,13 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
5073
5108
|
showSplashScreen && promises.push(new Promise(resolve =>
|
|
5074
5109
|
{
|
|
5075
5110
|
let t = 0;
|
|
5076
|
-
console.log(
|
|
5111
|
+
console.log(`${engineName} Engine v${engineVersion}`);
|
|
5077
5112
|
updateSplash();
|
|
5078
5113
|
function updateSplash()
|
|
5079
5114
|
{
|
|
5080
5115
|
clearInput();
|
|
5081
5116
|
drawEngineSplashScreen(t+=.01);
|
|
5082
|
-
t>1 ? resolve() : setTimeout(updateSplash,16);
|
|
5117
|
+
t>1 ? resolve() : setTimeout(updateSplash, 16);
|
|
5083
5118
|
}
|
|
5084
5119
|
}));
|
|
5085
5120
|
|
|
@@ -5140,7 +5175,7 @@ function engineObjectsDestroy()
|
|
|
5140
5175
|
|
|
5141
5176
|
/** Triggers a callback for each object within a given area
|
|
5142
5177
|
* @param {Vector2} [pos] - Center of test area
|
|
5143
|
-
* @param {Number} [size]
|
|
5178
|
+
* @param {Number|Vector2} [size] - Radius of circle if float, rectangle size if Vector2
|
|
5144
5179
|
* @param {Function} [callbackFunction] - Calls this function on every object that passes the test
|
|
5145
5180
|
* @param {Array} [objects=engineObjects] - List of objects to check
|
|
5146
5181
|
* @memberof Engine */
|
|
@@ -5151,7 +5186,7 @@ function engineObjectsCallback(pos, size, callbackFunction, objects=engineObject
|
|
|
5151
5186
|
for (const o of objects)
|
|
5152
5187
|
callbackFunction(o);
|
|
5153
5188
|
}
|
|
5154
|
-
else if (size
|
|
5189
|
+
else if (typeof size === 'object') // bounding box test
|
|
5155
5190
|
{
|
|
5156
5191
|
for (const o of objects)
|
|
5157
5192
|
isOverlapping(pos, size, o.pos, o.size) && callbackFunction(o);
|
|
@@ -5169,23 +5204,23 @@ function engineObjectsCallback(pos, size, callbackFunction, objects=engineObject
|
|
|
5169
5204
|
|
|
5170
5205
|
function drawEngineSplashScreen(t)
|
|
5171
5206
|
{
|
|
5172
|
-
const x =
|
|
5173
|
-
const w =
|
|
5174
|
-
const h =
|
|
5207
|
+
const x = overlayContext;
|
|
5208
|
+
const w = overlayCanvas.width = innerWidth;
|
|
5209
|
+
const h = overlayCanvas.height = innerHeight;
|
|
5210
|
+
|
|
5175
5211
|
{
|
|
5176
5212
|
// background
|
|
5177
5213
|
const p3 = percent(t, 1, .8);
|
|
5178
5214
|
const p4 = percent(t, 0, .5);
|
|
5179
5215
|
const g = x.createRadialGradient(w/2,h/2,0,w/2,h/2,Math.hypot(w,h)*.7);
|
|
5180
|
-
g.addColorStop(0,hsl(0,0,lerp(p4,0,p3/2),p3));
|
|
5181
|
-
g.addColorStop(1,hsl(0,0,0,p3));
|
|
5216
|
+
g.addColorStop(0,hsl(0,0,lerp(p4,0,p3/2),p3).toString());
|
|
5217
|
+
g.addColorStop(1,hsl(0,0,0,p3).toString());
|
|
5182
5218
|
x.save();
|
|
5183
5219
|
x.fillStyle = g;
|
|
5184
5220
|
x.fillRect(0,0,w,h);
|
|
5185
5221
|
}
|
|
5186
5222
|
|
|
5187
5223
|
// draw LittleJS logo...
|
|
5188
|
-
|
|
5189
5224
|
const rect = (X, Y, W, H, C)=>
|
|
5190
5225
|
{
|
|
5191
5226
|
x.beginPath();
|
|
@@ -5210,7 +5245,7 @@ function drawEngineSplashScreen(t)
|
|
|
5210
5245
|
C ? x.fill() : x.stroke();
|
|
5211
5246
|
};
|
|
5212
5247
|
const color = (c=0, l=0) =>
|
|
5213
|
-
hsl([.98,.3,.57,.14][c%4]-10,.8,[0,.3,.5,.8,.9][l]);
|
|
5248
|
+
hsl([.98,.3,.57,.14][c%4]-10,.8,[0,.3,.5,.8,.9][l]).toString();
|
|
5214
5249
|
const alpha = wave(1,1,t);
|
|
5215
5250
|
const p = percent(alpha, .1, .5);
|
|
5216
5251
|
|
|
@@ -5220,7 +5255,7 @@ function drawEngineSplashScreen(t)
|
|
|
5220
5255
|
x.scale(size,size);
|
|
5221
5256
|
x.translate(-40,-35);
|
|
5222
5257
|
x.lineJoin = x.lineCap = 'round';
|
|
5223
|
-
x.lineWidth = 1+p;
|
|
5258
|
+
x.lineWidth = .1 + p*1.9;
|
|
5224
5259
|
|
|
5225
5260
|
// drawing effect
|
|
5226
5261
|
const p2 = percent(alpha,.1,1);
|
|
@@ -5245,7 +5280,7 @@ function drawEngineSplashScreen(t)
|
|
|
5245
5280
|
|
|
5246
5281
|
// little stack
|
|
5247
5282
|
rect(37,14,9,6,color(3,2));
|
|
5248
|
-
rect(37,14,4,6,color(3,3));
|
|
5283
|
+
rect(37,14,4.5,6,color(3,3));
|
|
5249
5284
|
rect(37,14,9,6);
|
|
5250
5285
|
|
|
5251
5286
|
// big stack
|
|
@@ -5289,7 +5324,8 @@ function drawEngineSplashScreen(t)
|
|
|
5289
5324
|
x.lineTo(53+(1+i*2.9)*p,40);
|
|
5290
5325
|
x.lineTo(53+(4+i*3.5)*p,54);
|
|
5291
5326
|
x.fillStyle = color(0,i%2+2);
|
|
5292
|
-
x.fill()
|
|
5327
|
+
x.fill();
|
|
5328
|
+
i%2 && x.stroke();
|
|
5293
5329
|
}
|
|
5294
5330
|
|
|
5295
5331
|
// wheels
|
|
@@ -5312,7 +5348,7 @@ function drawEngineSplashScreen(t)
|
|
|
5312
5348
|
x.font = '900 16px arial';
|
|
5313
5349
|
x.textAlign = 'center';
|
|
5314
5350
|
x.textBaseline = 'top';
|
|
5315
|
-
x.lineWidth = 1+p*3;
|
|
5351
|
+
x.lineWidth = .1+p*3.9;
|
|
5316
5352
|
let w2 = 0;
|
|
5317
5353
|
for (let i=0; i<s.length; ++i)
|
|
5318
5354
|
w2 += x.measureText(s[i]).width;
|
|
@@ -5353,6 +5389,7 @@ export {
|
|
|
5353
5389
|
|
|
5354
5390
|
// Globals
|
|
5355
5391
|
debug,
|
|
5392
|
+
debugOverlay,
|
|
5356
5393
|
showWatermark,
|
|
5357
5394
|
|
|
5358
5395
|
// Debug
|