littlejsengine 1.8.9 → 1.9.0
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 +312 -250
- package/build/littlejs.esm.js +633 -599
- package/build/littlejs.esm.min.js +1 -1
- package/build/littlejs.js +632 -599
- package/build/littlejs.min.js +1 -1
- package/build/littlejs.release.js +567 -535
- 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/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/game.js +2 -2
- package/examples/starter/index.html +13 -13
- package/examples/stress/index.html +34 -26
- package/examples/typescript/index.html +1 -1
- 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 +47 -56
- package/src/engineExport.js +1 -0
- package/src/engineInput.js +57 -40
- package/src/engineMedals.js +32 -29
- package/src/engineObject.js +41 -26
- package/src/engineParticles.js +98 -72
- package/src/engineRelease.js +1 -1
- package/src/engineSettings.js +22 -22
- package/src/engineTileLayer.js +34 -33
- package/src/engineUtilities.js +44 -44
- package/src/engineWebGL.js +105 -126
- package/src/jsconfig.json +10 -0
package/build/littlejs.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
|
|
@@ -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=Vector2()] - World space position of the object
|
|
1561
|
+
* @param {Vector2} [size=Vector2(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=Color()] - 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} */
|
|
@@ -1847,7 +1863,7 @@ class EngineObject
|
|
|
1847
1863
|
/** Attaches a child to this with a given local transform
|
|
1848
1864
|
* @param {EngineObject} child
|
|
1849
1865
|
* @param {Vector2} [localPos=Vector2()]
|
|
1850
|
-
* @param {Number} [localAngle
|
|
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=Vector2()]
|
|
1967
|
-
* @param {(Number|Vector2)} [size=tileSizeDefault]
|
|
1968
|
-
* @param {Number} [textureIndex
|
|
1982
|
+
* @param {(Number|Vector2)} [pos=Vector2()] - 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)
|
|
@@ -2008,7 +2024,7 @@ class TileInfo
|
|
|
2008
2024
|
/** Create a tile info object
|
|
2009
2025
|
* @param {Vector2} [pos=Vector2()] - 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);
|
|
@@ -2083,23 +2102,21 @@ function worldToScreen(worldPos)
|
|
|
2083
2102
|
* @param {Vector2} pos - Center of the tile in world space
|
|
2084
2103
|
* @param {Vector2} [size=Vector2(1,1)] - Size of the tile in world space
|
|
2085
2104
|
* @param {TileInfo}[tileInfo] - Tile info to use, untextured if undefined
|
|
2086
|
-
* @param {Vector2} [tileSize=tileSizeDefault] - Tile size in source pixels
|
|
2087
2105
|
* @param {Color} [color=Color()] - Color to modulate with
|
|
2088
|
-
* @param {Number} [angle
|
|
2089
|
-
* @param {Boolean} [mirror
|
|
2106
|
+
* @param {Number} [angle] - Angle to rotate by
|
|
2107
|
+
* @param {Boolean} [mirror] - If true image is flipped along the Y axis
|
|
2090
2108
|
* @param {Color} [additiveColor=Color(0,0,0,0)] - Additive color to be applied
|
|
2091
2109
|
* @param {Boolean} [useWebGL=glEnable] - Use accelerated WebGL rendering
|
|
2092
|
-
* @param {Boolean} [screenSpace
|
|
2110
|
+
* @param {Boolean} [screenSpace] - If true the pos and size are in screen space
|
|
2093
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)
|
|
@@ -2160,49 +2178,38 @@ function drawTile(pos, size=vec2(1), tileInfo, color=new Color,
|
|
|
2160
2178
|
* @param {Vector2} pos
|
|
2161
2179
|
* @param {Vector2} [size=Vector2(1,1)]
|
|
2162
2180
|
* @param {Color} [color=Color()]
|
|
2163
|
-
* @param {Number} [angle
|
|
2181
|
+
* @param {Number} [angle]
|
|
2164
2182
|
* @param {Boolean} [useWebGL=glEnable]
|
|
2165
|
-
* @param {Boolean} [screenSpace
|
|
2183
|
+
* @param {Boolean} [screenSpace]
|
|
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
2193
|
* @param {Color} [color=Color()]
|
|
2176
|
-
* @param {Boolean} [
|
|
2177
|
-
* @param {
|
|
2178
|
-
* @param {CanvasRenderingContext2D} [context]
|
|
2194
|
+
* @param {Boolean} [screenSpace]
|
|
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
|
|
2209
|
+
* @param {Number} [thickness]
|
|
2203
2210
|
* @param {Color} [color=Color()]
|
|
2204
2211
|
* @param {Boolean} [useWebGL=glEnable]
|
|
2205
|
-
* @param {Boolean} [screenSpace
|
|
2212
|
+
* @param {Boolean} [screenSpace]
|
|
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]
|
|
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
|
|
2269
|
+
* @param {Number} [size]
|
|
2263
2270
|
* @param {Color} [color=Color()]
|
|
2264
|
-
* @param {Number} [lineWidth
|
|
2271
|
+
* @param {Number} [lineWidth]
|
|
2265
2272
|
* @param {Color} [lineColor=Color(0,0,0)]
|
|
2266
|
-
* @param {
|
|
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
|
|
2286
|
+
* @param {Number} [size]
|
|
2280
2287
|
* @param {Color} [color=Color()]
|
|
2281
|
-
* @param {Number} [lineWidth
|
|
2288
|
+
* @param {Number} [lineWidth]
|
|
2282
2289
|
* @param {Color} [lineColor=Color(0,0,0)]
|
|
2283
|
-
* @param {
|
|
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';
|
|
@@ -2323,8 +2330,8 @@ class FontImage
|
|
|
2323
2330
|
{
|
|
2324
2331
|
/** Create an image font
|
|
2325
2332
|
* @param {HTMLImageElement} [image] - Image for the font, if undefined default font is used
|
|
2326
|
-
* @param {Vector2} [tileSize=
|
|
2327
|
-
* @param {Vector2} [paddingSize=
|
|
2333
|
+
* @param {Vector2} [tileSize=Vector2(8)] - Size of the font source tiles
|
|
2334
|
+
* @param {Vector2} [paddingSize=Vector2(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; window.onmousemove(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)=> e.ctrlKey || (mouseWheel = 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} [pattern] - a single value in miliseconds 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
|
{
|
|
@@ -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=Vector2()] - World space position
|
|
3633
|
+
* @param {Vector2} [size=tileCollisionSize] - World space size
|
|
3634
|
+
* @param {TileInfo} [tileInfo] - Tile info for layer
|
|
3635
|
+
* @param {Vector2} [scale=Vector2(1,1)] - How much to scale this layer when rendered
|
|
3636
|
+
* @param {Number} [renderOrder] - Objects sorted by renderOrder before being rendered
|
|
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);
|
|
@@ -3674,16 +3698,17 @@ constructor(pos, size=tileCollisionSize, tileInfo=tile(), scale=vec2(1), renderO
|
|
|
3674
3698
|
*/
|
|
3675
3699
|
redraw()
|
|
3676
3700
|
{
|
|
3677
|
-
this.redrawStart(
|
|
3701
|
+
this.redrawStart(true);
|
|
3678
3702
|
this.drawAllTileData();
|
|
3679
3703
|
this.redrawEnd();
|
|
3680
3704
|
}
|
|
3681
3705
|
|
|
3682
3706
|
/** Call to start the redraw process
|
|
3683
|
-
* @param {Boolean} [clear
|
|
3684
|
-
redrawStart(clear
|
|
3707
|
+
* @param {Boolean} [clear] - Should it clear the canvas before drawing */
|
|
3708
|
+
redrawStart(clear=false)
|
|
3685
3709
|
{
|
|
3686
3710
|
// save current render settings
|
|
3711
|
+
/** @type {[HTMLCanvasElement, CanvasRenderingContext2D, Vector2, Vector2, number]} */
|
|
3687
3712
|
this.savedRenderSettings = [mainCanvas, mainContext, mainCanvasSize, cameraPos, cameraScale];
|
|
3688
3713
|
|
|
3689
3714
|
// hack: use normal rendering system to render the tiles
|
|
@@ -3706,8 +3731,8 @@ constructor(pos, size=tileCollisionSize, tileInfo=tile(), scale=vec2(1), renderO
|
|
|
3706
3731
|
/** Call to end the redraw process */
|
|
3707
3732
|
redrawEnd()
|
|
3708
3733
|
{
|
|
3709
|
-
ASSERT(mainContext == this.context
|
|
3710
|
-
glEnable && glCopyToContext(mainContext,
|
|
3734
|
+
ASSERT(mainContext == this.context, 'must call redrawStart() before drawing tiles');
|
|
3735
|
+
glEnable && glCopyToContext(mainContext, true);
|
|
3711
3736
|
//debugSaveCanvas(this.canvas);
|
|
3712
3737
|
|
|
3713
3738
|
// set stuff back to normal
|
|
@@ -3720,13 +3745,13 @@ constructor(pos, size=tileCollisionSize, tileInfo=tile(), scale=vec2(1), renderO
|
|
|
3720
3745
|
{
|
|
3721
3746
|
// first clear out where the tile was
|
|
3722
3747
|
const pos = layerPos.floor().add(this.pos).add(vec2(.5));
|
|
3723
|
-
this.drawCanvas2D(pos, vec2(1), 0,
|
|
3748
|
+
this.drawCanvas2D(pos, vec2(1), 0, false, (context)=>context.clearRect(-.5, -.5, 1, 1));
|
|
3724
3749
|
|
|
3725
3750
|
// draw the tile if not undefined
|
|
3726
3751
|
const d = this.getData(layerPos);
|
|
3727
3752
|
if (d.tile != undefined)
|
|
3728
3753
|
{
|
|
3729
|
-
ASSERT(mainContext == this.context
|
|
3754
|
+
ASSERT(mainContext == this.context, 'must call redrawStart() before drawing tiles');
|
|
3730
3755
|
const tileInfo = tile(d.tile, this.tileInfo.size, this.tileInfo.textureIndex);
|
|
3731
3756
|
drawTile(pos, vec2(1), tileInfo, d.color, d.direction*PI/2, d.mirror);
|
|
3732
3757
|
}
|
|
@@ -3794,7 +3819,7 @@ constructor(pos, size=tileCollisionSize, tileInfo=tile(), scale=vec2(1), renderO
|
|
|
3794
3819
|
* @param {Color} [color=Color()]
|
|
3795
3820
|
* @param {Number} [angle=0] */
|
|
3796
3821
|
drawRect(pos, size, color, angle)
|
|
3797
|
-
{ this.drawTile(pos, size,
|
|
3822
|
+
{ this.drawTile(pos, size, undefined, color, angle); }
|
|
3798
3823
|
}
|
|
3799
3824
|
/**
|
|
3800
3825
|
* LittleJS Particle System
|
|
@@ -3823,36 +3848,36 @@ class ParticleEmitter extends EngineObject
|
|
|
3823
3848
|
{
|
|
3824
3849
|
/** Create a particle system with the given settings
|
|
3825
3850
|
* @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}
|
|
3851
|
+
* @param {Number} [angle] - Angle to emit the particles
|
|
3852
|
+
* @param {Number|Vector2} [emitSize] - World space size of the emitter (float for circle diameter, vec2 for rect)
|
|
3853
|
+
* @param {Number} [emitTime] - How long to stay alive (0 is forever)
|
|
3854
|
+
* @param {Number} [emitRate] - How many particles per second to spawn, does not emit if 0
|
|
3855
|
+
* @param {Number} [emitConeAngle=PI] - Local angle to apply velocity to particles from emitter
|
|
3831
3856
|
* @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
|
|
3857
|
+
* @param {Color} [colorStartA=Color()] - Color at start of life 1, randomized between start colors
|
|
3858
|
+
* @param {Color} [colorStartB=Color()] - Color at start of life 2, randomized between start colors
|
|
3859
|
+
* @param {Color} [colorEndA=Color(1,1,1,0)] - Color at end of life 1, randomized between end colors
|
|
3860
|
+
* @param {Color} [colorEndB=Color(1,1,1,0)] - Color at end of life 2, randomized between end colors
|
|
3861
|
+
* @param {Number} [particleTime] - How long particles live
|
|
3862
|
+
* @param {Number} [sizeStart] - How big are particles at start
|
|
3863
|
+
* @param {Number} [sizeEnd] - How big are particles at end
|
|
3864
|
+
* @param {Number} [speed] - How fast are particles when spawned
|
|
3865
|
+
* @param {Number} [angleSpeed] - How fast are particles rotating
|
|
3866
|
+
* @param {Number} [damping] - How much to dampen particle speed
|
|
3867
|
+
* @param {Number} [angleDamping] - How much to dampen particle angular speed
|
|
3868
|
+
* @param {Number} [gravityScale] - How much gravity effect particles
|
|
3869
|
+
* @param {Number} [particleConeAngle] - Cone for start particle angle
|
|
3870
|
+
* @param {Number} [fadeRate] - How quick to fade particles at start/end in percent of life
|
|
3871
|
+
* @param {Number} [randomness] - Apply extra randomness percent
|
|
3872
|
+
* @param {Boolean} [collideTiles] - Do particles collide against tiles
|
|
3873
|
+
* @param {Boolean} [additive] - Should particles use addtive blend
|
|
3874
|
+
* @param {Boolean} [randomColorLinear] - Should color be randomized linearly or across each component
|
|
3875
|
+
* @param {Number} [renderOrder] - Render order for particles (additive is above other stuff by default)
|
|
3876
|
+
* @param {Boolean} [localSpace] - Should it be in local space of emitter (world space is default)
|
|
3852
3877
|
*/
|
|
3853
3878
|
constructor
|
|
3854
3879
|
(
|
|
3855
|
-
|
|
3880
|
+
position,
|
|
3856
3881
|
angle,
|
|
3857
3882
|
emitSize = 0,
|
|
3858
3883
|
emitTime = 0,
|
|
@@ -3874,14 +3899,14 @@ class ParticleEmitter extends EngineObject
|
|
|
3874
3899
|
particleConeAngle = PI,
|
|
3875
3900
|
fadeRate = .1,
|
|
3876
3901
|
randomness = .2,
|
|
3877
|
-
collideTiles,
|
|
3878
|
-
additive,
|
|
3879
|
-
randomColorLinear =
|
|
3902
|
+
collideTiles = false,
|
|
3903
|
+
additive = false,
|
|
3904
|
+
randomColorLinear = true,
|
|
3880
3905
|
renderOrder = additive ? 1e9 : 0,
|
|
3881
|
-
localSpace
|
|
3906
|
+
localSpace = false
|
|
3882
3907
|
)
|
|
3883
3908
|
{
|
|
3884
|
-
super(
|
|
3909
|
+
super(position, vec2(), tileInfo, angle, undefined, renderOrder);
|
|
3885
3910
|
|
|
3886
3911
|
// emitter settings
|
|
3887
3912
|
/** @property {Number|Vector2} - World space size of the emitter (float for circle diameter, vec2 for rect) */
|
|
@@ -3930,14 +3955,17 @@ class ParticleEmitter extends EngineObject
|
|
|
3930
3955
|
this.randomness = randomness;
|
|
3931
3956
|
/** @property {Boolean} - Do particles collide against tiles */
|
|
3932
3957
|
this.collideTiles = collideTiles;
|
|
3933
|
-
/** @property {
|
|
3958
|
+
/** @property {Boolean} - Should particles use addtive blend */
|
|
3934
3959
|
this.additive = additive;
|
|
3935
3960
|
/** @property {Boolean} - Should it be in local space of emitter */
|
|
3936
|
-
this.localSpace
|
|
3937
|
-
/** @property {Number} - If
|
|
3961
|
+
this.localSpace = localSpace;
|
|
3962
|
+
/** @property {Number} - If non zero the partile is drawn as a trail, stretched in the drection of velocity */
|
|
3938
3963
|
this.trailScale = 0;
|
|
3939
|
-
|
|
3940
|
-
|
|
3964
|
+
/** @property {Function} - Callback when particle is destroyed */
|
|
3965
|
+
this.particleDestroyCallback = undefined;
|
|
3966
|
+
/** @property {Function} - Callback when particle is created */
|
|
3967
|
+
this.particleCreateCallback = undefined;
|
|
3968
|
+
/** @property {Number} - Track particle emit time */
|
|
3941
3969
|
this.emitTimeBuffer = 0;
|
|
3942
3970
|
}
|
|
3943
3971
|
|
|
@@ -3969,18 +3997,16 @@ class ParticleEmitter extends EngineObject
|
|
|
3969
3997
|
emitParticle()
|
|
3970
3998
|
{
|
|
3971
3999
|
// spawn a particle
|
|
3972
|
-
let pos = this.emitSize
|
|
3973
|
-
|
|
3974
|
-
|
|
3975
|
-
|
|
4000
|
+
let pos = typeof this.emitSize === 'number' ? // check if number was used
|
|
4001
|
+
randInCircle(this.emitSize/2) // circle emitter
|
|
4002
|
+
: vec2(rand(-.5,.5), rand(-.5,.5)) // box emitter
|
|
4003
|
+
.multiply(this.emitSize).rotate(this.angle)
|
|
3976
4004
|
let angle = rand(this.particleConeAngle, -this.particleConeAngle);
|
|
3977
4005
|
if (!this.localSpace)
|
|
3978
4006
|
{
|
|
3979
4007
|
pos = this.pos.add(pos);
|
|
3980
4008
|
angle += this.angle;
|
|
3981
4009
|
}
|
|
3982
|
-
|
|
3983
|
-
const particle = new Particle(pos, this.tileInfo, this.tileSize, angle);
|
|
3984
4010
|
|
|
3985
4011
|
// randomness scales each paremeter by a percentage
|
|
3986
4012
|
const randomness = this.randomness;
|
|
@@ -3996,30 +4022,21 @@ class ParticleEmitter extends EngineObject
|
|
|
3996
4022
|
const colorStart = randColor(this.colorStartA, this.colorStartB, this.randomColorLinear);
|
|
3997
4023
|
const colorEnd = randColor(this.colorEndA, this.colorEndB, this.randomColorLinear);
|
|
3998
4024
|
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;
|
|
4025
|
+
|
|
4026
|
+
// build particle
|
|
4027
|
+
const particle = new Particle(pos, this.tileInfo, angle, colorStart, colorEnd, particleTime, sizeStart, sizeEnd, this.fadeRate, this.additive, this.trailScale, this.localSpace && this, this.particleDestroyCallback);
|
|
4028
|
+
particle.velocity = vec2().setAngle(velocityAngle, speed);
|
|
4029
|
+
particle.fadeRate = this.fadeRate;
|
|
4030
|
+
particle.damping = this.damping;
|
|
4031
|
+
particle.angleDamping = this.angleDamping;
|
|
4032
|
+
particle.elasticity = this.elasticity;
|
|
4033
|
+
particle.friction = this.friction;
|
|
4034
|
+
particle.gravityScale = this.gravityScale;
|
|
4035
|
+
particle.collideTiles = this.collideTiles;
|
|
4036
|
+
particle.renderOrder = this.renderOrder;
|
|
4037
|
+
particle.mirror = !!randInt(2);
|
|
4038
|
+
|
|
4039
|
+
// call particle create callaback
|
|
4023
4040
|
this.particleCreateCallback && this.particleCreateCallback(particle);
|
|
4024
4041
|
|
|
4025
4042
|
// return the newly created particle
|
|
@@ -4038,13 +4055,47 @@ class ParticleEmitter extends EngineObject
|
|
|
4038
4055
|
class Particle extends EngineObject
|
|
4039
4056
|
{
|
|
4040
4057
|
/**
|
|
4041
|
-
* Create a particle with the given
|
|
4042
|
-
* @param {Vector2}
|
|
4043
|
-
* @param {TileInfo} [tileInfo]
|
|
4044
|
-
* @param {Number}
|
|
4058
|
+
* Create a particle with the given shis.colorStart = undefined;ettings
|
|
4059
|
+
* @param {Vector2} position - World space position of the particle
|
|
4060
|
+
* @param {TileInfo} [tileInfo] - Tile info to render particles
|
|
4061
|
+
* @param {Number} [angle] - Angle to rotate the particle
|
|
4062
|
+
* @param {Color} [colorStart] - Color at start of life
|
|
4063
|
+
* @param {Color} [colorEnd] - Color at end of life
|
|
4064
|
+
* @param {Number} [lifeTime] - How long to live for
|
|
4065
|
+
* @param {Number} [sizeStart] - Angle to rotate the particle
|
|
4066
|
+
* @param {Number} [sizeEnd] - Angle to rotate the particle
|
|
4067
|
+
* @param {Number} [fadeRate] - Angle to rotate the particle
|
|
4068
|
+
* @param {Boolean} [additive] - Angle to rotate the particle
|
|
4069
|
+
* @param {Number} [trailScale] - If a trail, how long to make it
|
|
4070
|
+
* @param {ParticleEmitter} [localSpaceEmitter] - Parent emitter if local space
|
|
4071
|
+
* @param {Function} [destroyCallback] - Called when particle dies
|
|
4045
4072
|
*/
|
|
4046
|
-
constructor(
|
|
4047
|
-
|
|
4073
|
+
constructor(position, tileInfo, angle, colorStart, colorEnd, lifeTime, sizeStart, sizeEnd, fadeRate, additive, trailScale, localSpaceEmitter, destroyCallback
|
|
4074
|
+
)
|
|
4075
|
+
{
|
|
4076
|
+
super(position, vec2(), tileInfo, angle);
|
|
4077
|
+
|
|
4078
|
+
/** @property {Color} - Color at start of life */
|
|
4079
|
+
this.colorStart = colorStart;
|
|
4080
|
+
/** @property {Color} - Calculated change in color */
|
|
4081
|
+
this.colorEndDelta = colorEnd.subtract(colorStart);
|
|
4082
|
+
/** @property {Number} - How long to live for */
|
|
4083
|
+
this.lifeTime = lifeTime;
|
|
4084
|
+
/** @property {Number} - Size at start of life */
|
|
4085
|
+
this.sizeStart = sizeStart;
|
|
4086
|
+
/** @property {Number} - Calculated change in size */
|
|
4087
|
+
this.sizeEndDelta = sizeEnd - sizeStart;
|
|
4088
|
+
/** @property {Number} - How quick to fade in/out */
|
|
4089
|
+
this.fadeRate = fadeRate;
|
|
4090
|
+
/** @property {Boolean} - Is it additive */
|
|
4091
|
+
this.additive = additive;
|
|
4092
|
+
/** @property {Number} - If a trail, how long to make it */
|
|
4093
|
+
this.trailScale = trailScale;
|
|
4094
|
+
/** @property {ParticleEmitter} - Parent emitter if local space */
|
|
4095
|
+
this.localSpaceEmitter = localSpaceEmitter;
|
|
4096
|
+
/** @property {Function} - Called when particle dies */
|
|
4097
|
+
this.destroyCallback = destroyCallback;
|
|
4098
|
+
}
|
|
4048
4099
|
|
|
4049
4100
|
/** Render the particle, automatically called each frame, sorted by renderOrder */
|
|
4050
4101
|
render()
|
|
@@ -4062,7 +4113,7 @@ class Particle extends EngineObject
|
|
|
4062
4113
|
(p < fadeRate ? p/fadeRate : p > 1-fadeRate ? (1-p)/fadeRate : 1)); // fade alpha
|
|
4063
4114
|
|
|
4064
4115
|
// draw the particle
|
|
4065
|
-
this.additive && setBlendMode(
|
|
4116
|
+
this.additive && setBlendMode(true);
|
|
4066
4117
|
|
|
4067
4118
|
let pos = this.pos, angle = this.angle;
|
|
4068
4119
|
if (this.localSpaceEmitter)
|
|
@@ -4152,7 +4203,7 @@ class Medal
|
|
|
4152
4203
|
* @param {Number} id - The unique identifier of the medal
|
|
4153
4204
|
* @param {String} name - Name of the medal
|
|
4154
4205
|
* @param {String} [description] - Description of the medal
|
|
4155
|
-
* @param {String} [icon
|
|
4206
|
+
* @param {String} [icon] - Icon for the medal
|
|
4156
4207
|
* @param {String} [src] - Image location for the medal
|
|
4157
4208
|
*/
|
|
4158
4209
|
constructor(id, name, description='', icon='🏆', src)
|
|
@@ -4175,14 +4226,14 @@ class Medal
|
|
|
4175
4226
|
return;
|
|
4176
4227
|
|
|
4177
4228
|
// save the medal
|
|
4178
|
-
ASSERT(medalsSaveName
|
|
4229
|
+
ASSERT(medalsSaveName, 'save name must be set');
|
|
4179
4230
|
localStorage[this.storageKey()] = this.unlocked = 1;
|
|
4180
4231
|
medalsDisplayQueue.push(this);
|
|
4181
4232
|
newgrounds && newgrounds.unlockMedal(this.id);
|
|
4182
4233
|
}
|
|
4183
4234
|
|
|
4184
4235
|
/** Render a medal
|
|
4185
|
-
* @param {Number} [hidePercent
|
|
4236
|
+
* @param {Number} [hidePercent] - How much to slide the medal off screen
|
|
4186
4237
|
*/
|
|
4187
4238
|
render(hidePercent=0)
|
|
4188
4239
|
{
|
|
@@ -4194,25 +4245,25 @@ class Medal
|
|
|
4194
4245
|
// draw containing rect and clip to that region
|
|
4195
4246
|
context.save();
|
|
4196
4247
|
context.beginPath();
|
|
4197
|
-
context.fillStyle =
|
|
4198
|
-
context.strokeStyle =
|
|
4248
|
+
context.fillStyle = rgb(.9,.9,.9).toString();
|
|
4249
|
+
context.strokeStyle = rgb(0,0,0).toString();
|
|
4199
4250
|
context.lineWidth = 3;
|
|
4200
|
-
context.
|
|
4251
|
+
context.rect(x, y, width, medalDisplaySize.y);
|
|
4252
|
+
context.fill();
|
|
4201
4253
|
context.stroke();
|
|
4202
4254
|
context.clip();
|
|
4203
4255
|
|
|
4204
4256
|
// draw the icon and text
|
|
4205
4257
|
this.renderIcon(vec2(x+15+medalDisplayIconSize/2, y+medalDisplaySize.y/2));
|
|
4206
4258
|
const pos = vec2(x+medalDisplayIconSize+30, y+28);
|
|
4207
|
-
drawTextScreen(this.name, pos, 38, new Color(0,0,0), 0,
|
|
4259
|
+
drawTextScreen(this.name, pos, 38, new Color(0,0,0), 0, undefined, 'left');
|
|
4208
4260
|
pos.y += 32;
|
|
4209
|
-
drawTextScreen(this.description, pos, 24, new Color(0,0,0), 0,
|
|
4261
|
+
drawTextScreen(this.description, pos, 24, new Color(0,0,0), 0, undefined, 'left');
|
|
4210
4262
|
context.restore();
|
|
4211
4263
|
}
|
|
4212
4264
|
|
|
4213
4265
|
/** Render the icon for a medal
|
|
4214
|
-
* @param {
|
|
4215
|
-
* @param {Number} y - Screen space Y position
|
|
4266
|
+
* @param {Vector2} pos - Screen space position
|
|
4216
4267
|
* @param {Number} [size=medalDisplayIconSize] - Screen space size
|
|
4217
4268
|
*/
|
|
4218
4269
|
renderIcon(pos, size=medalDisplayIconSize)
|
|
@@ -4240,7 +4291,10 @@ function medalsRender()
|
|
|
4240
4291
|
if (!medalsDisplayTimeLast)
|
|
4241
4292
|
medalsDisplayTimeLast = timeReal;
|
|
4242
4293
|
else if (time > medalDisplayTime)
|
|
4243
|
-
|
|
4294
|
+
{
|
|
4295
|
+
medalsDisplayTimeLast = 0;
|
|
4296
|
+
medalsDisplayQueue.shift();
|
|
4297
|
+
}
|
|
4244
4298
|
else
|
|
4245
4299
|
{
|
|
4246
4300
|
// slide on/off medals
|
|
@@ -4280,8 +4334,8 @@ class Newgrounds
|
|
|
4280
4334
|
* @param {Object} [cryptoJS] - An instance of CryptoJS, if there is a cipher */
|
|
4281
4335
|
constructor(app_id, cipher, cryptoJS)
|
|
4282
4336
|
{
|
|
4283
|
-
ASSERT(!newgrounds && app_id
|
|
4284
|
-
ASSERT(!cipher || cryptoJS
|
|
4337
|
+
ASSERT(!newgrounds && app_id>0, 'there can only be one newgrounds object');
|
|
4338
|
+
ASSERT(!cipher || cryptoJS, 'must provide cryptojs if there is a cipher');
|
|
4285
4339
|
|
|
4286
4340
|
this.app_id = app_id;
|
|
4287
4341
|
this.cipher = cipher;
|
|
@@ -4324,39 +4378,39 @@ class Newgrounds
|
|
|
4324
4378
|
debugMedals && console.log(this.scoreboards);
|
|
4325
4379
|
|
|
4326
4380
|
const keepAliveMS = 5 * 60 * 1e3;
|
|
4327
|
-
setInterval(()=>this.call('Gateway.ping', 0,
|
|
4381
|
+
setInterval(()=>this.call('Gateway.ping', 0, true), keepAliveMS);
|
|
4328
4382
|
}
|
|
4329
4383
|
|
|
4330
4384
|
/** Send message to unlock a medal by id
|
|
4331
4385
|
* @param {Number} id - The medal id */
|
|
4332
|
-
unlockMedal(id) { return this.call('Medal.unlock', {'id':id},
|
|
4386
|
+
unlockMedal(id) { return this.call('Medal.unlock', {'id':id}, true); }
|
|
4333
4387
|
|
|
4334
4388
|
/** Send message to post score
|
|
4335
4389
|
* @param {Number} id - The scoreboard id
|
|
4336
4390
|
* @param {Number} value - The score value */
|
|
4337
|
-
postScore(id, value) { return this.call('ScoreBoard.postScore', {'id':id, 'value':value},
|
|
4391
|
+
postScore(id, value) { return this.call('ScoreBoard.postScore', {'id':id, 'value':value}, true); }
|
|
4338
4392
|
|
|
4339
4393
|
/** 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}
|
|
4394
|
+
* @param {Number} id - The scoreboard id
|
|
4395
|
+
* @param {String} [user] - A user's id or name
|
|
4396
|
+
* @param {Number} [social] - If true, only social scores will be loaded
|
|
4397
|
+
* @param {Number} [skip] - Number of scores to skip before start
|
|
4398
|
+
* @param {Number} [limit] - Number of scores to include in the list
|
|
4399
|
+
* @return {Object} - The response JSON object
|
|
4346
4400
|
*/
|
|
4347
|
-
getScores(id, user
|
|
4401
|
+
getScores(id, user, social=0, skip=0, limit=10)
|
|
4348
4402
|
{ return this.call('ScoreBoard.getScores', {'id':id, 'user':user, 'social':social, 'skip':skip, 'limit':limit}); }
|
|
4349
4403
|
|
|
4350
4404
|
/** Send message to log a view */
|
|
4351
|
-
logView() { return this.call('App.logView', {'host':this.host},
|
|
4405
|
+
logView() { return this.call('App.logView', {'host':this.host}, true); }
|
|
4352
4406
|
|
|
4353
4407
|
/** 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}
|
|
4408
|
+
* @param {String} component - Name of the component
|
|
4409
|
+
* @param {Object} [parameters] - Parameters to use for call
|
|
4410
|
+
* @param {Boolean} [async] - If true, don't wait for response before continuing
|
|
4411
|
+
* @return {Object} - The response JSON object
|
|
4358
4412
|
*/
|
|
4359
|
-
call(component, parameters
|
|
4413
|
+
call(component, parameters, async=false)
|
|
4360
4414
|
{
|
|
4361
4415
|
const call = {'component':component, 'parameters':parameters};
|
|
4362
4416
|
if (this.cipher)
|
|
@@ -4391,7 +4445,7 @@ class Newgrounds
|
|
|
4391
4445
|
return xmlHttp.responseText && JSON.parse(xmlHttp.responseText);
|
|
4392
4446
|
}
|
|
4393
4447
|
}
|
|
4394
|
-
/**
|
|
4448
|
+
/**
|
|
4395
4449
|
* LittleJS WebGL Interface
|
|
4396
4450
|
* - All webgl used by the engine is wrapped up here
|
|
4397
4451
|
* - For normal stuff you won't need to see or call anything in this file
|
|
@@ -4410,13 +4464,13 @@ class Newgrounds
|
|
|
4410
4464
|
* @memberof WebGL */
|
|
4411
4465
|
let glCanvas;
|
|
4412
4466
|
|
|
4413
|
-
/** 2d context for glCanvas
|
|
4414
|
-
* @type {
|
|
4467
|
+
/** 2d context for glCanvas
|
|
4468
|
+
* @type {WebGL2RenderingContext}
|
|
4415
4469
|
* @memberof WebGL */
|
|
4416
4470
|
let glContext;
|
|
4417
4471
|
|
|
4418
4472
|
// WebGL internal variables not exposed to documentation
|
|
4419
|
-
let
|
|
4473
|
+
let glShader, glActiveTexture, glArrayBuffer, glGeometryBuffer, glPositionData, glColorData, glInstanceCount, glAdditive, glBatchAdditive;
|
|
4420
4474
|
|
|
4421
4475
|
///////////////////////////////////////////////////////////////////////////////
|
|
4422
4476
|
|
|
@@ -4432,73 +4486,89 @@ function glInit()
|
|
|
4432
4486
|
|
|
4433
4487
|
// setup vertex and fragment shaders
|
|
4434
4488
|
glShader = glCreateProgram(
|
|
4435
|
-
'#version 300 es\n' +
|
|
4436
|
-
'precision highp float;'+
|
|
4437
|
-
'uniform mat4 m;'+
|
|
4438
|
-
'in
|
|
4439
|
-
'
|
|
4440
|
-
'
|
|
4441
|
-
'
|
|
4442
|
-
'
|
|
4443
|
-
'
|
|
4489
|
+
'#version 300 es\n' + // specify GLSL ES version
|
|
4490
|
+
'precision highp float;'+ // use highp for better accuracy
|
|
4491
|
+
'uniform mat4 m;'+ // transform matrix
|
|
4492
|
+
'in vec2 g;'+ // geometry
|
|
4493
|
+
'in vec4 p,u,c,a;'+ // position/size, uvs, color, additiveColor
|
|
4494
|
+
'in float r;'+ // rotation
|
|
4495
|
+
'out vec2 v;'+ // return uv, color, additiveColor
|
|
4496
|
+
'out vec4 d,e;'+ // return uv, color, additiveColor
|
|
4497
|
+
'void main(){'+ // shader entry point
|
|
4498
|
+
'vec2 s=(g-.5)*p.zw;'+ // get size offset
|
|
4499
|
+
'gl_Position=m*vec4(p.xy+s*cos(r)-vec2(-s.y,s)*sin(r),1,1);'+ // transform position
|
|
4500
|
+
'v=mix(u.xw,u.zy,g);'+ // pass uv to fragment shader
|
|
4501
|
+
'd=c;e=a;'+ // pass colors to fragment shader
|
|
4502
|
+
'}' // end of shader
|
|
4444
4503
|
,
|
|
4445
|
-
'#version 300 es\n' +
|
|
4446
|
-
'precision highp float;'+
|
|
4447
|
-
'in
|
|
4448
|
-
'
|
|
4449
|
-
'
|
|
4450
|
-
'
|
|
4451
|
-
'
|
|
4452
|
-
'
|
|
4504
|
+
'#version 300 es\n' + // specify GLSL ES version
|
|
4505
|
+
'precision highp float;'+ // use highp for better accuracy
|
|
4506
|
+
'in vec2 v;'+ // uv
|
|
4507
|
+
'in vec4 d,e;'+ // color, additiveColor
|
|
4508
|
+
'uniform sampler2D s;'+ // texture
|
|
4509
|
+
'out vec4 c;'+ // out color
|
|
4510
|
+
'void main(){'+ // shader entry point
|
|
4511
|
+
'c=texture(s,v)*d+e;'+ // modulate texture by color plus additive
|
|
4512
|
+
'}' // end of shader
|
|
4453
4513
|
);
|
|
4454
4514
|
|
|
4455
4515
|
// init buffers
|
|
4456
|
-
|
|
4457
|
-
glPositionData = new Float32Array(
|
|
4458
|
-
glColorData = new Uint32Array(
|
|
4516
|
+
const glInstanceData = new ArrayBuffer(gl_INSTANCE_BUFFER_SIZE);
|
|
4517
|
+
glPositionData = new Float32Array(glInstanceData);
|
|
4518
|
+
glColorData = new Uint32Array(glInstanceData);
|
|
4459
4519
|
glArrayBuffer = glContext.createBuffer();
|
|
4460
|
-
|
|
4520
|
+
glGeometryBuffer = glContext.createBuffer();
|
|
4521
|
+
|
|
4522
|
+
// create the geometry buffer, triangle strip square
|
|
4523
|
+
const geometry = new Float32Array([glInstanceCount=0,0,1,0,0,1,1,1]);
|
|
4524
|
+
glContext.bindBuffer(gl_ARRAY_BUFFER, glGeometryBuffer);
|
|
4525
|
+
glContext.bufferData(gl_ARRAY_BUFFER, geometry, gl_STATIC_DRAW);
|
|
4461
4526
|
}
|
|
4462
4527
|
|
|
4463
4528
|
// Setup render each frame, called automatically by engine
|
|
4464
4529
|
function glPreRender()
|
|
4465
4530
|
{
|
|
4466
4531
|
// clear and set to same size as main canvas
|
|
4467
|
-
glContext.viewport(0, 0, glCanvas.width
|
|
4532
|
+
glContext.viewport(0, 0, glCanvas.width=mainCanvas.width, glCanvas.height=mainCanvas.height);
|
|
4468
4533
|
glContext.clear(gl_COLOR_BUFFER_BIT);
|
|
4469
4534
|
|
|
4470
4535
|
// set up the shader
|
|
4471
4536
|
glContext.useProgram(glShader);
|
|
4472
4537
|
glContext.activeTexture(gl_TEXTURE0);
|
|
4473
4538
|
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
|
-
|
|
4539
|
+
|
|
4478
4540
|
// set vertex attributes
|
|
4479
|
-
let offset = 0;
|
|
4480
|
-
|
|
4541
|
+
let offset = glAdditive = glBatchAdditive = 0;
|
|
4542
|
+
let initVertexAttribArray = (name, type, typeSize, size)=>
|
|
4481
4543
|
{
|
|
4482
4544
|
const location = glContext.getAttribLocation(glShader, name);
|
|
4545
|
+
const stride = typeSize && gl_INSTANCE_BYTE_STRIDE; // only if not geometry
|
|
4546
|
+
const divisor = typeSize && 1; // only if not geometry
|
|
4547
|
+
const normalize = typeSize==1; // only if color
|
|
4483
4548
|
glContext.enableVertexAttribArray(location);
|
|
4484
|
-
glContext.vertexAttribPointer(location, size, type, normalize,
|
|
4549
|
+
glContext.vertexAttribPointer(location, size, type, normalize, stride, offset);
|
|
4550
|
+
glContext.vertexAttribDivisor(location, divisor);
|
|
4485
4551
|
offset += size*typeSize;
|
|
4486
4552
|
}
|
|
4487
|
-
|
|
4488
|
-
initVertexAttribArray('
|
|
4489
|
-
|
|
4553
|
+
glContext.bindBuffer(gl_ARRAY_BUFFER, glGeometryBuffer);
|
|
4554
|
+
initVertexAttribArray('g', gl_FLOAT, 0, 2); // geometry
|
|
4555
|
+
glContext.bindBuffer(gl_ARRAY_BUFFER, glArrayBuffer);
|
|
4556
|
+
glContext.bufferData(gl_ARRAY_BUFFER, gl_INSTANCE_BUFFER_SIZE, gl_DYNAMIC_DRAW);
|
|
4557
|
+
initVertexAttribArray('p', gl_FLOAT, 4, 4); // position & size
|
|
4558
|
+
initVertexAttribArray('u', gl_FLOAT, 4, 4); // texture coords
|
|
4559
|
+
initVertexAttribArray('c', gl_UNSIGNED_BYTE, 1, 4); // color
|
|
4560
|
+
initVertexAttribArray('a', gl_UNSIGNED_BYTE, 1, 4); // additiveColor
|
|
4561
|
+
initVertexAttribArray('r', gl_FLOAT, 4, 1); // rotation
|
|
4490
4562
|
|
|
4491
4563
|
// build the transform matrix
|
|
4492
|
-
const
|
|
4493
|
-
const
|
|
4494
|
-
|
|
4495
|
-
const cy = -1 - sy*cameraPos.y;
|
|
4496
|
-
glContext.uniformMatrix4fv(glContext.getUniformLocation(glShader, 'm'), 0,
|
|
4564
|
+
const s = vec2(2*cameraScale).divide(mainCanvasSize);
|
|
4565
|
+
const p = vec2(-1).subtract(cameraPos.multiply(s));
|
|
4566
|
+
glContext.uniformMatrix4fv(glContext.getUniformLocation(glShader, 'm'), false,
|
|
4497
4567
|
new Float32Array([
|
|
4498
|
-
|
|
4499
|
-
|
|
4500
|
-
|
|
4501
|
-
|
|
4568
|
+
s.x, 0, 0, 0,
|
|
4569
|
+
0, s.y, 0, 0,
|
|
4570
|
+
1, 1, 1, 1,
|
|
4571
|
+
p.x, p.y, 0, 0
|
|
4502
4572
|
])
|
|
4503
4573
|
);
|
|
4504
4574
|
}
|
|
@@ -4519,7 +4589,7 @@ function glSetTexture(texture)
|
|
|
4519
4589
|
|
|
4520
4590
|
/** Compile WebGL shader of the given type, will throw errors if in debug mode
|
|
4521
4591
|
* @param {String} source
|
|
4522
|
-
* @param
|
|
4592
|
+
* @param {Number} type
|
|
4523
4593
|
* @return {WebGLShader}
|
|
4524
4594
|
* @memberof WebGL */
|
|
4525
4595
|
function glCompileShader(source, type)
|
|
@@ -4536,8 +4606,8 @@ function glCompileShader(source, type)
|
|
|
4536
4606
|
}
|
|
4537
4607
|
|
|
4538
4608
|
/** Create WebGL program with given shaders
|
|
4539
|
-
* @param {
|
|
4540
|
-
* @param {
|
|
4609
|
+
* @param {String} vsSource
|
|
4610
|
+
* @param {String} fsSource
|
|
4541
4611
|
* @return {WebGLProgram}
|
|
4542
4612
|
* @memberof WebGL */
|
|
4543
4613
|
function glCreateProgram(vsSource, fsSource)
|
|
@@ -4555,7 +4625,7 @@ function glCreateProgram(vsSource, fsSource)
|
|
|
4555
4625
|
}
|
|
4556
4626
|
|
|
4557
4627
|
/** Create WebGL texture from an image and init the texture settings
|
|
4558
|
-
* @param {
|
|
4628
|
+
* @param {HTMLImageElement} image
|
|
4559
4629
|
* @return {WebGLTexture}
|
|
4560
4630
|
* @memberof WebGL */
|
|
4561
4631
|
function glCreateTexture(image)
|
|
@@ -4565,7 +4635,7 @@ function glCreateTexture(image)
|
|
|
4565
4635
|
glContext.bindTexture(gl_TEXTURE_2D, texture);
|
|
4566
4636
|
if (image)
|
|
4567
4637
|
glContext.texImage2D(gl_TEXTURE_2D, 0, gl_RGBA, gl_RGBA, gl_UNSIGNED_BYTE, image);
|
|
4568
|
-
|
|
4638
|
+
|
|
4569
4639
|
// use point filtering for pixelated rendering
|
|
4570
4640
|
const filter = canvasPixelated ? gl_NEAREST : gl_LINEAR;
|
|
4571
4641
|
glContext.texParameteri(gl_TEXTURE_2D, gl_TEXTURE_MIN_FILTER, filter);
|
|
@@ -4580,29 +4650,31 @@ function glCreateTexture(image)
|
|
|
4580
4650
|
* @memberof WebGL */
|
|
4581
4651
|
function glFlush()
|
|
4582
4652
|
{
|
|
4583
|
-
if (!
|
|
4653
|
+
if (!glInstanceCount) return;
|
|
4584
4654
|
|
|
4585
4655
|
const destBlend = glBatchAdditive ? gl_ONE : gl_ONE_MINUS_SRC_ALPHA;
|
|
4586
4656
|
glContext.blendFuncSeparate(gl_SRC_ALPHA, destBlend, gl_ONE, destBlend);
|
|
4587
4657
|
glContext.enable(gl_BLEND);
|
|
4588
4658
|
|
|
4589
4659
|
// draw all the sprites in the batch and reset the buffer
|
|
4590
|
-
glContext.bufferSubData(gl_ARRAY_BUFFER, 0,
|
|
4591
|
-
glContext.
|
|
4592
|
-
|
|
4660
|
+
glContext.bufferSubData(gl_ARRAY_BUFFER, 0, glPositionData);
|
|
4661
|
+
glContext.drawArraysInstanced(gl_TRIANGLE_STRIP, 0, 4, glInstanceCount);
|
|
4662
|
+
if (showWatermark)
|
|
4663
|
+
drawCount += glInstanceCount;
|
|
4664
|
+
glInstanceCount = 0;
|
|
4593
4665
|
glBatchAdditive = glAdditive;
|
|
4594
4666
|
}
|
|
4595
4667
|
|
|
4596
4668
|
/** Draw any sprites still in the buffer, copy to main canvas and clear
|
|
4597
4669
|
* @param {CanvasRenderingContext2D} context
|
|
4598
|
-
* @param {Boolean} [forceDraw
|
|
4670
|
+
* @param {Boolean} [forceDraw]
|
|
4599
4671
|
* @memberof WebGL */
|
|
4600
|
-
function glCopyToContext(context, forceDraw)
|
|
4672
|
+
function glCopyToContext(context, forceDraw=false)
|
|
4601
4673
|
{
|
|
4602
|
-
if (!
|
|
4603
|
-
|
|
4674
|
+
if (!glInstanceCount && !forceDraw) return;
|
|
4675
|
+
|
|
4604
4676
|
glFlush();
|
|
4605
|
-
|
|
4677
|
+
|
|
4606
4678
|
// do not draw in overlay mode because the canvas is visible
|
|
4607
4679
|
if (!glOverlay || forceDraw)
|
|
4608
4680
|
context.drawImage(glCanvas, 0, 0);
|
|
@@ -4624,60 +4696,22 @@ function glCopyToContext(context, forceDraw)
|
|
|
4624
4696
|
function glDraw(x, y, sizeX, sizeY, angle, uv0X, uv0Y, uv1X, uv1Y, rgba, rgbaAdditive=0)
|
|
4625
4697
|
{
|
|
4626
4698
|
// flush if there is not enough room or if different blend mode
|
|
4627
|
-
|
|
4628
|
-
if (glBatchCount >= gl_MAX_BATCH-vertCount || glBatchAdditive != glAdditive)
|
|
4699
|
+
if (glInstanceCount >= gl_MAX_INSTANCES-1 || glBatchAdditive != glAdditive)
|
|
4629
4700
|
glFlush();
|
|
4630
4701
|
|
|
4631
|
-
|
|
4632
|
-
|
|
4633
|
-
|
|
4634
|
-
|
|
4635
|
-
[
|
|
4636
|
-
|
|
4637
|
-
|
|
4638
|
-
|
|
4639
|
-
|
|
4640
|
-
];
|
|
4641
|
-
|
|
4642
|
-
|
|
4643
|
-
|
|
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
|
-
}
|
|
4655
|
-
|
|
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
|
-
// flush if there is not enough room or if different blend mode
|
|
4663
|
-
const vertCount = points.length + 2;
|
|
4664
|
-
if (glBatchCount >= gl_MAX_BATCH-vertCount || glBatchAdditive != glAdditive)
|
|
4665
|
-
glFlush();
|
|
4666
|
-
|
|
4667
|
-
// setup triangle strip from list of points
|
|
4668
|
-
for(let i = vertCount, offset = glBatchCount * gl_INDICIES_PER_VERT; i--;)
|
|
4669
|
-
{
|
|
4670
|
-
const j = clamp(i-1, 0, vertCount-3); // degenerate tri at ends
|
|
4671
|
-
const h = j>>1;
|
|
4672
|
-
const point = points[j%2? h : vertCount-3-h];
|
|
4673
|
-
glPositionData[offset++] = point.x;
|
|
4674
|
-
glPositionData[offset++] = point.y;
|
|
4675
|
-
glPositionData[offset++] = 0; // uvx
|
|
4676
|
-
glPositionData[offset++] = 0; // uvy
|
|
4677
|
-
glColorData[offset++] = 0; // nothing to tint
|
|
4678
|
-
glColorData[offset++] = rgba; // apply rgba via additive
|
|
4679
|
-
}
|
|
4680
|
-
glBatchCount += vertCount;
|
|
4702
|
+
let offset = glInstanceCount * gl_INDICIES_PER_INSTANCE;
|
|
4703
|
+
glPositionData[offset++] = x;
|
|
4704
|
+
glPositionData[offset++] = y;
|
|
4705
|
+
glPositionData[offset++] = sizeX;
|
|
4706
|
+
glPositionData[offset++] = sizeY;
|
|
4707
|
+
glPositionData[offset++] = uv0X;
|
|
4708
|
+
glPositionData[offset++] = uv0Y;
|
|
4709
|
+
glPositionData[offset++] = uv1X;
|
|
4710
|
+
glPositionData[offset++] = uv1Y;
|
|
4711
|
+
glColorData[offset++] = rgba;
|
|
4712
|
+
glColorData[offset++] = rgbaAdditive;
|
|
4713
|
+
glPositionData[offset++] = angle;
|
|
4714
|
+
glInstanceCount++;
|
|
4681
4715
|
}
|
|
4682
4716
|
|
|
4683
4717
|
///////////////////////////////////////////////////////////////////////////////
|
|
@@ -4691,7 +4725,7 @@ let glPostShader, glPostArrayBuffer, glPostTexture, glPostIncludeOverlay;
|
|
|
4691
4725
|
* @memberof WebGL */
|
|
4692
4726
|
function glInitPostProcess(shaderCode, includeOverlay)
|
|
4693
4727
|
{
|
|
4694
|
-
ASSERT(!glPostShader
|
|
4728
|
+
ASSERT(!glPostShader, 'can only have 1 post effects shader');
|
|
4695
4729
|
|
|
4696
4730
|
if (!shaderCode) // default shader pass through
|
|
4697
4731
|
shaderCode = 'void mainImage(out vec4 c,vec2 p){c=texture(iChannel0,p/iResolution.xy);}';
|
|
@@ -4702,14 +4736,14 @@ function glInitPostProcess(shaderCode, includeOverlay)
|
|
|
4702
4736
|
'precision highp float;'+ // use highp for better accuracy
|
|
4703
4737
|
'in vec2 p;'+ // position
|
|
4704
4738
|
'void main(){'+ // shader entry point
|
|
4705
|
-
'gl_Position=vec4(p
|
|
4739
|
+
'gl_Position=vec4(p+p-1.,1,1);'+ // set position
|
|
4706
4740
|
'}' // end of shader
|
|
4707
4741
|
,
|
|
4708
4742
|
'#version 300 es\n' + // specify GLSL ES version
|
|
4709
4743
|
'precision highp float;'+ // use highp for better accuracy
|
|
4710
4744
|
'uniform sampler2D iChannel0;'+ // input texture
|
|
4711
4745
|
'uniform vec3 iResolution;'+ // size of output texture
|
|
4712
|
-
'uniform float iTime;'+ // time
|
|
4746
|
+
'uniform float iTime;'+ // time
|
|
4713
4747
|
'out vec4 c;'+ // out color
|
|
4714
4748
|
'\n' + shaderCode + '\n'+ // insert custom shader code
|
|
4715
4749
|
'void main(){'+ // shader entry point
|
|
@@ -4720,7 +4754,7 @@ function glInitPostProcess(shaderCode, includeOverlay)
|
|
|
4720
4754
|
|
|
4721
4755
|
// create buffer and texture
|
|
4722
4756
|
glPostArrayBuffer = glContext.createBuffer();
|
|
4723
|
-
glPostTexture = glCreateTexture();
|
|
4757
|
+
glPostTexture = glCreateTexture(undefined);
|
|
4724
4758
|
glPostIncludeOverlay = includeOverlay;
|
|
4725
4759
|
|
|
4726
4760
|
// hide the original 2d canvas
|
|
@@ -4756,10 +4790,9 @@ function glRenderPostProcess()
|
|
|
4756
4790
|
|
|
4757
4791
|
// setup shader program to draw one triangle
|
|
4758
4792
|
glContext.useProgram(glPostShader);
|
|
4793
|
+
glContext.bindBuffer(gl_ARRAY_BUFFER, glGeometryBuffer);
|
|
4794
|
+
glContext.pixelStorei(gl_UNPACK_FLIP_Y_WEBGL, 1);
|
|
4759
4795
|
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
4796
|
|
|
4764
4797
|
// set textures, pass in the 2d canvas and gl canvas in separate texture channels
|
|
4765
4798
|
glContext.activeTexture(gl_TEXTURE0);
|
|
@@ -4770,19 +4803,19 @@ function glRenderPostProcess()
|
|
|
4770
4803
|
const vertexByteStride = 8;
|
|
4771
4804
|
const pLocation = glContext.getAttribLocation(glPostShader, 'p');
|
|
4772
4805
|
glContext.enableVertexAttribArray(pLocation);
|
|
4773
|
-
glContext.vertexAttribPointer(pLocation, 2, gl_FLOAT,
|
|
4806
|
+
glContext.vertexAttribPointer(pLocation, 2, gl_FLOAT, false, vertexByteStride, 0);
|
|
4774
4807
|
|
|
4775
4808
|
// set uniforms and draw
|
|
4776
4809
|
const uniformLocation = (name)=>glContext.getUniformLocation(glPostShader, name);
|
|
4777
4810
|
glContext.uniform1i(uniformLocation('iChannel0'), 0);
|
|
4778
4811
|
glContext.uniform1f(uniformLocation('iTime'), time);
|
|
4779
4812
|
glContext.uniform3f(uniformLocation('iResolution'), mainCanvas.width, mainCanvas.height, 1);
|
|
4780
|
-
glContext.drawArrays(gl_TRIANGLE_STRIP, 0,
|
|
4813
|
+
glContext.drawArrays(gl_TRIANGLE_STRIP, 0, 4);
|
|
4781
4814
|
}
|
|
4782
4815
|
|
|
4783
4816
|
///////////////////////////////////////////////////////////////////////////////
|
|
4784
4817
|
// store gl constants as integers so their name doesn't use space in minifed
|
|
4785
|
-
const
|
|
4818
|
+
const
|
|
4786
4819
|
gl_ONE = 1,
|
|
4787
4820
|
gl_TRIANGLE_STRIP = 5,
|
|
4788
4821
|
gl_SRC_ALPHA = 770,
|
|
@@ -4804,17 +4837,17 @@ gl_TEXTURE0 = 33984,
|
|
|
4804
4837
|
gl_ARRAY_BUFFER = 34962,
|
|
4805
4838
|
gl_STATIC_DRAW = 35044,
|
|
4806
4839
|
gl_DYNAMIC_DRAW = 35048,
|
|
4807
|
-
gl_FRAGMENT_SHADER = 35632,
|
|
4840
|
+
gl_FRAGMENT_SHADER = 35632,
|
|
4808
4841
|
gl_VERTEX_SHADER = 35633,
|
|
4809
4842
|
gl_COMPILE_STATUS = 35713,
|
|
4810
4843
|
gl_LINK_STATUS = 35714,
|
|
4811
4844
|
gl_UNPACK_FLIP_Y_WEBGL = 37440,
|
|
4812
4845
|
|
|
4813
4846
|
// constants for batch rendering
|
|
4814
|
-
|
|
4815
|
-
|
|
4816
|
-
|
|
4817
|
-
|
|
4847
|
+
gl_INDICIES_PER_INSTANCE = 11,
|
|
4848
|
+
gl_MAX_INSTANCES = 1e4,
|
|
4849
|
+
gl_INSTANCE_BYTE_STRIDE = gl_INDICIES_PER_INSTANCE * 4, // 11 * 4
|
|
4850
|
+
gl_INSTANCE_BUFFER_SIZE = gl_MAX_INSTANCES * gl_INSTANCE_BYTE_STRIDE;
|
|
4818
4851
|
/**
|
|
4819
4852
|
* LittleJS - The Tiny JavaScript Game Engine That Can!
|
|
4820
4853
|
* MIT License - Copyright 2021 Frank Force
|
|
@@ -4847,7 +4880,7 @@ const engineName = 'LittleJS';
|
|
|
4847
4880
|
* @type {String}
|
|
4848
4881
|
* @default
|
|
4849
4882
|
* @memberof Engine */
|
|
4850
|
-
const engineVersion = '1.
|
|
4883
|
+
const engineVersion = '1.9.0';
|
|
4851
4884
|
|
|
4852
4885
|
/** Frames per second to update objects
|
|
4853
4886
|
* @type {Number}
|
|
@@ -4888,14 +4921,14 @@ let timeReal = 0;
|
|
|
4888
4921
|
|
|
4889
4922
|
/** Is the game paused? Causes time and objects to not be updated
|
|
4890
4923
|
* @type {Boolean}
|
|
4891
|
-
* @default
|
|
4924
|
+
* @default false
|
|
4892
4925
|
* @memberof Engine */
|
|
4893
|
-
let paused =
|
|
4926
|
+
let paused = false;
|
|
4894
4927
|
|
|
4895
4928
|
/** Set if game is paused
|
|
4896
|
-
* @param {Boolean}
|
|
4929
|
+
* @param {Boolean} isPaused
|
|
4897
4930
|
* @memberof Engine */
|
|
4898
|
-
function setPaused(
|
|
4931
|
+
function setPaused(isPaused) { paused = isPaused; }
|
|
4899
4932
|
|
|
4900
4933
|
// Frame time tracking
|
|
4901
4934
|
let frameTimeLastMS = 0, frameTimeBufferMS = 0, averageFPS = 0;
|
|
@@ -4912,7 +4945,7 @@ let frameTimeLastMS = 0, frameTimeBufferMS = 0, averageFPS = 0;
|
|
|
4912
4945
|
* @memberof Engine */
|
|
4913
4946
|
function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRenderPost, imageSources=['tiles.png'])
|
|
4914
4947
|
{
|
|
4915
|
-
ASSERT(Array.isArray(imageSources)
|
|
4948
|
+
ASSERT(Array.isArray(imageSources), 'pass in images as array');
|
|
4916
4949
|
|
|
4917
4950
|
// internal update loop for engine
|
|
4918
4951
|
function engineUpdate(frameTimeMS=0)
|
|
@@ -4922,12 +4955,12 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
4922
4955
|
frameTimeLastMS = frameTimeMS;
|
|
4923
4956
|
if (debug || showWatermark)
|
|
4924
4957
|
averageFPS = lerp(.05, averageFPS, 1e3/(frameTimeDeltaMS||1));
|
|
4925
|
-
const debugSpeedUp = debug && keyIsDown(
|
|
4926
|
-
const debugSpeedDown = debug && keyIsDown(
|
|
4958
|
+
const debugSpeedUp = debug && keyIsDown('Equal'); // +
|
|
4959
|
+
const debugSpeedDown = debug && keyIsDown('Minus'); // -
|
|
4927
4960
|
if (debug) // +/- to speed/slow time
|
|
4928
4961
|
frameTimeDeltaMS *= debugSpeedUp ? 5 : debugSpeedDown ? .2 : 1;
|
|
4929
4962
|
timeReal += frameTimeDeltaMS / 1e3;
|
|
4930
|
-
frameTimeBufferMS +=
|
|
4963
|
+
frameTimeBufferMS += paused ? 0 : frameTimeDeltaMS;
|
|
4931
4964
|
if (!debugSpeedUp)
|
|
4932
4965
|
frameTimeBufferMS = min(frameTimeBufferMS, 50); // clamp incase of slow framerate
|
|
4933
4966
|
|
|
@@ -5037,7 +5070,7 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
5037
5070
|
'user-select:none;' + // prevent mobile hold to select
|
|
5038
5071
|
'-webkit-user-select:none;' + // compatibility for ios
|
|
5039
5072
|
'-webkit-touch-callout:none'; // compatibility for ios
|
|
5040
|
-
document.body.style = styleBody;
|
|
5073
|
+
document.body.style.cssText = styleBody;
|
|
5041
5074
|
document.body.appendChild(mainCanvas = document.createElement('canvas'));
|
|
5042
5075
|
mainContext = mainCanvas.getContext('2d');
|
|
5043
5076
|
|
|
@@ -5050,10 +5083,9 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
5050
5083
|
overlayContext = overlayCanvas.getContext('2d');
|
|
5051
5084
|
|
|
5052
5085
|
// set canvas style
|
|
5053
|
-
const styleCanvas =
|
|
5054
|
-
'position:absolute;' + // position
|
|
5086
|
+
const styleCanvas = 'position:absolute;' + // position
|
|
5055
5087
|
'top:50%;left:50%;transform:translate(-50%,-50%)'; // center
|
|
5056
|
-
(glCanvas||mainCanvas).style = mainCanvas.style = overlayCanvas.style = styleCanvas;
|
|
5088
|
+
(glCanvas||mainCanvas).style.cssText = mainCanvas.style.cssText = overlayCanvas.style.cssText = styleCanvas;
|
|
5057
5089
|
|
|
5058
5090
|
// create promises for loading images
|
|
5059
5091
|
const promises = imageSources.map((src, textureIndex)=>
|
|
@@ -5073,13 +5105,13 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
5073
5105
|
showSplashScreen && promises.push(new Promise(resolve =>
|
|
5074
5106
|
{
|
|
5075
5107
|
let t = 0;
|
|
5076
|
-
console.log(
|
|
5108
|
+
console.log(`${engineName} Engine v${engineVersion}`);
|
|
5077
5109
|
updateSplash();
|
|
5078
5110
|
function updateSplash()
|
|
5079
5111
|
{
|
|
5080
5112
|
clearInput();
|
|
5081
5113
|
drawEngineSplashScreen(t+=.01);
|
|
5082
|
-
t>1 ? resolve() : setTimeout(updateSplash,16);
|
|
5114
|
+
t>1 ? resolve() : setTimeout(updateSplash, 16);
|
|
5083
5115
|
}
|
|
5084
5116
|
}));
|
|
5085
5117
|
|
|
@@ -5140,7 +5172,7 @@ function engineObjectsDestroy()
|
|
|
5140
5172
|
|
|
5141
5173
|
/** Triggers a callback for each object within a given area
|
|
5142
5174
|
* @param {Vector2} [pos] - Center of test area
|
|
5143
|
-
* @param {Number} [size]
|
|
5175
|
+
* @param {Number|Vector2} [size] - Radius of circle if float, rectangle size if Vector2
|
|
5144
5176
|
* @param {Function} [callbackFunction] - Calls this function on every object that passes the test
|
|
5145
5177
|
* @param {Array} [objects=engineObjects] - List of objects to check
|
|
5146
5178
|
* @memberof Engine */
|
|
@@ -5151,7 +5183,7 @@ function engineObjectsCallback(pos, size, callbackFunction, objects=engineObject
|
|
|
5151
5183
|
for (const o of objects)
|
|
5152
5184
|
callbackFunction(o);
|
|
5153
5185
|
}
|
|
5154
|
-
else if (size
|
|
5186
|
+
else if (typeof size === 'object') // bounding box test
|
|
5155
5187
|
{
|
|
5156
5188
|
for (const o of objects)
|
|
5157
5189
|
isOverlapping(pos, size, o.pos, o.size) && callbackFunction(o);
|
|
@@ -5169,23 +5201,23 @@ function engineObjectsCallback(pos, size, callbackFunction, objects=engineObject
|
|
|
5169
5201
|
|
|
5170
5202
|
function drawEngineSplashScreen(t)
|
|
5171
5203
|
{
|
|
5172
|
-
const x =
|
|
5173
|
-
const w =
|
|
5174
|
-
const h =
|
|
5204
|
+
const x = overlayContext;
|
|
5205
|
+
const w = overlayCanvas.width = innerWidth;
|
|
5206
|
+
const h = overlayCanvas.height = innerHeight;
|
|
5207
|
+
|
|
5175
5208
|
{
|
|
5176
5209
|
// background
|
|
5177
5210
|
const p3 = percent(t, 1, .8);
|
|
5178
5211
|
const p4 = percent(t, 0, .5);
|
|
5179
5212
|
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));
|
|
5213
|
+
g.addColorStop(0,hsl(0,0,lerp(p4,0,p3/2),p3).toString());
|
|
5214
|
+
g.addColorStop(1,hsl(0,0,0,p3).toString());
|
|
5182
5215
|
x.save();
|
|
5183
5216
|
x.fillStyle = g;
|
|
5184
5217
|
x.fillRect(0,0,w,h);
|
|
5185
5218
|
}
|
|
5186
5219
|
|
|
5187
5220
|
// draw LittleJS logo...
|
|
5188
|
-
|
|
5189
5221
|
const rect = (X, Y, W, H, C)=>
|
|
5190
5222
|
{
|
|
5191
5223
|
x.beginPath();
|
|
@@ -5210,7 +5242,7 @@ function drawEngineSplashScreen(t)
|
|
|
5210
5242
|
C ? x.fill() : x.stroke();
|
|
5211
5243
|
};
|
|
5212
5244
|
const color = (c=0, l=0) =>
|
|
5213
|
-
hsl([.98,.3,.57,.14][c%4]-10,.8,[0,.3,.5,.8,.9][l]);
|
|
5245
|
+
hsl([.98,.3,.57,.14][c%4]-10,.8,[0,.3,.5,.8,.9][l]).toString();
|
|
5214
5246
|
const alpha = wave(1,1,t);
|
|
5215
5247
|
const p = percent(alpha, .1, .5);
|
|
5216
5248
|
|
|
@@ -5220,7 +5252,7 @@ function drawEngineSplashScreen(t)
|
|
|
5220
5252
|
x.scale(size,size);
|
|
5221
5253
|
x.translate(-40,-35);
|
|
5222
5254
|
x.lineJoin = x.lineCap = 'round';
|
|
5223
|
-
x.lineWidth = 1+p;
|
|
5255
|
+
x.lineWidth = .1 + p*1.9;
|
|
5224
5256
|
|
|
5225
5257
|
// drawing effect
|
|
5226
5258
|
const p2 = percent(alpha,.1,1);
|
|
@@ -5245,7 +5277,7 @@ function drawEngineSplashScreen(t)
|
|
|
5245
5277
|
|
|
5246
5278
|
// little stack
|
|
5247
5279
|
rect(37,14,9,6,color(3,2));
|
|
5248
|
-
rect(37,14,4,6,color(3,3));
|
|
5280
|
+
rect(37,14,4.5,6,color(3,3));
|
|
5249
5281
|
rect(37,14,9,6);
|
|
5250
5282
|
|
|
5251
5283
|
// big stack
|
|
@@ -5289,7 +5321,8 @@ function drawEngineSplashScreen(t)
|
|
|
5289
5321
|
x.lineTo(53+(1+i*2.9)*p,40);
|
|
5290
5322
|
x.lineTo(53+(4+i*3.5)*p,54);
|
|
5291
5323
|
x.fillStyle = color(0,i%2+2);
|
|
5292
|
-
x.fill()
|
|
5324
|
+
x.fill();
|
|
5325
|
+
i%2 && x.stroke();
|
|
5293
5326
|
}
|
|
5294
5327
|
|
|
5295
5328
|
// wheels
|
|
@@ -5312,7 +5345,7 @@ function drawEngineSplashScreen(t)
|
|
|
5312
5345
|
x.font = '900 16px arial';
|
|
5313
5346
|
x.textAlign = 'center';
|
|
5314
5347
|
x.textBaseline = 'top';
|
|
5315
|
-
x.lineWidth = 1+p*3;
|
|
5348
|
+
x.lineWidth = .1+p*3.9;
|
|
5316
5349
|
let w2 = 0;
|
|
5317
5350
|
for (let i=0; i<s.length; ++i)
|
|
5318
5351
|
w2 += x.measureText(s[i]).width;
|