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
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
let showWatermark = 0;
|
|
14
|
-
let debugKey =
|
|
14
|
+
let debugKey = '';
|
|
15
15
|
const debug = 0;
|
|
16
16
|
const debugOverlay = 0;
|
|
17
17
|
const debugPhysics = 0;
|
|
@@ -79,15 +79,15 @@ function sign(value) { return Math.sign(value); }
|
|
|
79
79
|
|
|
80
80
|
/** Returns first parm modulo the second param, but adjusted so negative numbers work as expected
|
|
81
81
|
* @param {Number} dividend
|
|
82
|
-
* @param {Number} [divisor
|
|
82
|
+
* @param {Number} [divisor]
|
|
83
83
|
* @return {Number}
|
|
84
84
|
* @memberof Utilities */
|
|
85
85
|
function mod(dividend, divisor=1) { return ((dividend % divisor) + divisor) % divisor; }
|
|
86
86
|
|
|
87
87
|
/** Clamps the value beween max and min
|
|
88
88
|
* @param {Number} value
|
|
89
|
-
* @param {Number} [min
|
|
90
|
-
* @param {Number} [max
|
|
89
|
+
* @param {Number} [min]
|
|
90
|
+
* @param {Number} [max]
|
|
91
91
|
* @return {Number}
|
|
92
92
|
* @memberof Utilities */
|
|
93
93
|
function clamp(value, min=0, max=1) { return value < min ? min : value > max ? max : value; }
|
|
@@ -112,7 +112,7 @@ function lerp(percent, valueA, valueB) { return valueA + clamp(percent) * (value
|
|
|
112
112
|
/** Returns signed wrapped distance between the two values passed in
|
|
113
113
|
* @param {Number} valueA
|
|
114
114
|
* @param {Number} valueB
|
|
115
|
-
* @param {Number} [wrapSize
|
|
115
|
+
* @param {Number} [wrapSize]
|
|
116
116
|
* @returns {Number}
|
|
117
117
|
* @memberof Utilities */
|
|
118
118
|
function distanceWrap(valueA, valueB, wrapSize=1)
|
|
@@ -122,7 +122,7 @@ function distanceWrap(valueA, valueB, wrapSize=1)
|
|
|
122
122
|
* @param {Number} percent
|
|
123
123
|
* @param {Number} valueA
|
|
124
124
|
* @param {Number} valueB
|
|
125
|
-
* @param {Number} [wrapSize
|
|
125
|
+
* @param {Number} [wrapSize]
|
|
126
126
|
* @returns {Number}
|
|
127
127
|
* @memberof Utilities */
|
|
128
128
|
function lerpWrap(percent, valueA, valueB, wrapSize=1)
|
|
@@ -133,7 +133,7 @@ function lerpWrap(percent, valueA, valueB, wrapSize=1)
|
|
|
133
133
|
* @param {Number} angleB
|
|
134
134
|
* @returns {Number}
|
|
135
135
|
* @memberof Utilities */
|
|
136
|
-
function distanceAngle(angleA, angleB) { distanceWrap(angleA, angleB, 2*PI); }
|
|
136
|
+
function distanceAngle(angleA, angleB) { return distanceWrap(angleA, angleB, 2*PI); }
|
|
137
137
|
|
|
138
138
|
/** Linearly interpolates between the angles passed in with wrappping
|
|
139
139
|
* @param {Number} percent
|
|
@@ -169,10 +169,10 @@ function isOverlapping(pointA, sizeA, pointB, sizeB)
|
|
|
169
169
|
}
|
|
170
170
|
|
|
171
171
|
/** Returns an oscillating wave between 0 and amplitude with frequency of 1 Hz by default
|
|
172
|
-
* @param {Number} [frequency
|
|
173
|
-
* @param {Number} [amplitude
|
|
174
|
-
* @param {Number} [t=time]
|
|
175
|
-
* @return {Number}
|
|
172
|
+
* @param {Number} [frequency] - Frequency of the wave in Hz
|
|
173
|
+
* @param {Number} [amplitude] - Amplitude (max height) of the wave
|
|
174
|
+
* @param {Number} [t=time] - Value to use for time of the wave
|
|
175
|
+
* @return {Number} - Value waving between 0 and amplitude
|
|
176
176
|
* @memberof Utilities */
|
|
177
177
|
function wave(frequency=1, amplitude=1, t=time)
|
|
178
178
|
{ return amplitude/2 * (1 - Math.cos(t*frequency*2*PI)); }
|
|
@@ -189,15 +189,15 @@ function formatTime(t) { return (t/60|0) + ':' + (t%60<10?'0':'') + (t%60|0); }
|
|
|
189
189
|
* @namespace Random */
|
|
190
190
|
|
|
191
191
|
/** Returns a random value between the two values passed in
|
|
192
|
-
* @param {Number} [valueA
|
|
193
|
-
* @param {Number} [valueB
|
|
192
|
+
* @param {Number} [valueA]
|
|
193
|
+
* @param {Number} [valueB]
|
|
194
194
|
* @return {Number}
|
|
195
195
|
* @memberof Random */
|
|
196
196
|
function rand(valueA=1, valueB=0) { return valueB + Math.random() * (valueA-valueB); }
|
|
197
197
|
|
|
198
198
|
/** Returns a floored random value the two values passed in
|
|
199
199
|
* @param {Number} valueA
|
|
200
|
-
* @param {Number} [valueB
|
|
200
|
+
* @param {Number} [valueB]
|
|
201
201
|
* @return {Number}
|
|
202
202
|
* @memberof Random */
|
|
203
203
|
function randInt(valueA, valueB=0) { return Math.floor(rand(valueA,valueB)); }
|
|
@@ -208,14 +208,14 @@ function randInt(valueA, valueB=0) { return Math.floor(rand(valueA,valueB)); }
|
|
|
208
208
|
function randSign() { return randInt(2) * 2 - 1; }
|
|
209
209
|
|
|
210
210
|
/** Returns a random Vector2 with the passed in length
|
|
211
|
-
* @param {Number} [length
|
|
211
|
+
* @param {Number} [length]
|
|
212
212
|
* @return {Vector2}
|
|
213
213
|
* @memberof Random */
|
|
214
214
|
function randVector(length=1) { return new Vector2().setAngle(rand(2*PI), length); }
|
|
215
215
|
|
|
216
216
|
/** Returns a random Vector2 within a circular shape
|
|
217
|
-
* @param {Number} [radius
|
|
218
|
-
* @param {Number} [minRadius
|
|
217
|
+
* @param {Number} [radius]
|
|
218
|
+
* @param {Number} [minRadius]
|
|
219
219
|
* @return {Vector2}
|
|
220
220
|
* @memberof Random */
|
|
221
221
|
function randInCircle(radius=1, minRadius=0)
|
|
@@ -227,7 +227,7 @@ function randInCircle(radius=1, minRadius=0)
|
|
|
227
227
|
* @param {Boolean} [linear]
|
|
228
228
|
* @return {Color}
|
|
229
229
|
* @memberof Random */
|
|
230
|
-
function randColor(colorA=new Color, colorB=new Color(0,0,0,1), linear)
|
|
230
|
+
function randColor(colorA=new Color, colorB=new Color(0,0,0,1), linear=false)
|
|
231
231
|
{
|
|
232
232
|
return linear ? colorA.lerp(colorB, rand()) :
|
|
233
233
|
new Color(rand(colorA.r,colorB.r), rand(colorA.g,colorB.g), rand(colorA.b,colorB.b), rand(colorA.a,colorB.a));
|
|
@@ -256,8 +256,8 @@ class RandomGenerator
|
|
|
256
256
|
}
|
|
257
257
|
|
|
258
258
|
/** Returns a seeded random value between the two values passed in
|
|
259
|
-
* @param {Number} [valueA
|
|
260
|
-
* @param {Number} [valueB
|
|
259
|
+
* @param {Number} [valueA]
|
|
260
|
+
* @param {Number} [valueB]
|
|
261
261
|
* @return {Number} */
|
|
262
262
|
float(valueA=1, valueB=0)
|
|
263
263
|
{
|
|
@@ -270,7 +270,7 @@ class RandomGenerator
|
|
|
270
270
|
|
|
271
271
|
/** Returns a floored seeded random value the two values passed in
|
|
272
272
|
* @param {Number} valueA
|
|
273
|
-
* @param {Number} [valueB
|
|
273
|
+
* @param {Number} [valueB]
|
|
274
274
|
* @return {Number} */
|
|
275
275
|
int(valueA, valueB=0) { return Math.floor(this.float(valueA, valueB)); }
|
|
276
276
|
|
|
@@ -283,8 +283,8 @@ class RandomGenerator
|
|
|
283
283
|
|
|
284
284
|
/**
|
|
285
285
|
* Create a 2d vector, can take another Vector2 to copy, 2 scalars, or 1 scalar
|
|
286
|
-
* @param {(Number|Vector2)} [x
|
|
287
|
-
* @param {Number} [y
|
|
286
|
+
* @param {(Number|Vector2)} [x]
|
|
287
|
+
* @param {Number} [y]
|
|
288
288
|
* @return {Vector2}
|
|
289
289
|
* @example
|
|
290
290
|
* let a = vec2(0, 1); // vector with coordinates (0, 1)
|
|
@@ -294,15 +294,15 @@ class RandomGenerator
|
|
|
294
294
|
* @memberof Utilities
|
|
295
295
|
*/
|
|
296
296
|
function vec2(x=0, y)
|
|
297
|
-
{ return x
|
|
297
|
+
{ return typeof x === 'number'? new Vector2(x, y == undefined? x : y) : new Vector2(x.x, x.y); }
|
|
298
298
|
|
|
299
299
|
/**
|
|
300
300
|
* Check if object is a valid Vector2
|
|
301
|
-
* @param {
|
|
301
|
+
* @param {any} v
|
|
302
302
|
* @return {Boolean}
|
|
303
303
|
* @memberof Utilities
|
|
304
304
|
*/
|
|
305
|
-
function isVector2(v) { return
|
|
305
|
+
function isVector2(v) { return typeof v === 'object' && typeof v.x === 'number' && typeof v.y === 'number'; }
|
|
306
306
|
|
|
307
307
|
/**
|
|
308
308
|
* 2D Vector object with vector math library
|
|
@@ -316,8 +316,8 @@ function isVector2(v) { return !isNaN(v.x) && !isNaN(v.y); }
|
|
|
316
316
|
class Vector2
|
|
317
317
|
{
|
|
318
318
|
/** Create a 2D vector with the x and y passed in, can also be created with vec2()
|
|
319
|
-
* @param {Number} [x
|
|
320
|
-
* @param {Number} [y
|
|
319
|
+
* @param {Number} [x] - X axis location
|
|
320
|
+
* @param {Number} [y] - Y axis location */
|
|
321
321
|
constructor(x=0, y=0)
|
|
322
322
|
{
|
|
323
323
|
/** @property {Number} - X axis location */
|
|
@@ -374,12 +374,12 @@ class Vector2
|
|
|
374
374
|
distanceSquared(v) { return (this.x - v.x)**2 + (this.y - v.y)**2; }
|
|
375
375
|
|
|
376
376
|
/** Returns a new vector in same direction as this one with the length passed in
|
|
377
|
-
* @param {Number} [length
|
|
377
|
+
* @param {Number} [length]
|
|
378
378
|
* @return {Vector2} */
|
|
379
379
|
normalize(length=1) { const l = this.length(); return l ? this.scale(length/l) : new Vector2(0, length); }
|
|
380
380
|
|
|
381
381
|
/** Returns a new vector clamped to length passed in
|
|
382
|
-
* @param {Number} [length
|
|
382
|
+
* @param {Number} [length]
|
|
383
383
|
* @return {Vector2} */
|
|
384
384
|
clampLength(length=1) { const l = this.length(); return l > length ? this.scale(length/l) : this; }
|
|
385
385
|
|
|
@@ -398,8 +398,8 @@ class Vector2
|
|
|
398
398
|
angle() { return Math.atan2(this.x, this.y); }
|
|
399
399
|
|
|
400
400
|
/** Sets this vector with angle and length passed in
|
|
401
|
-
* @param {Number} [angle
|
|
402
|
-
* @param {Number} [length
|
|
401
|
+
* @param {Number} [angle]
|
|
402
|
+
* @param {Number} [length]
|
|
403
403
|
* @return {Vector2} */
|
|
404
404
|
setAngle(angle=0, length=1)
|
|
405
405
|
{ this.x = length*Math.sin(angle); this.y = length*Math.cos(angle); return this; }
|
|
@@ -484,10 +484,10 @@ function hsl(h, s, l, a) { return new Color().setHSLA(h, s, l, a); }
|
|
|
484
484
|
class Color
|
|
485
485
|
{
|
|
486
486
|
/** Create a color with the rgba components passed in, white by default
|
|
487
|
-
* @param {Number} [r
|
|
488
|
-
* @param {Number} [g
|
|
489
|
-
* @param {Number} [b
|
|
490
|
-
* @param {Number} [a
|
|
487
|
+
* @param {Number} [r] - red
|
|
488
|
+
* @param {Number} [g] - green
|
|
489
|
+
* @param {Number} [b] - blue
|
|
490
|
+
* @param {Number} [a] - alpha*/
|
|
491
491
|
constructor(r=1, g=1, b=1, a=1)
|
|
492
492
|
{
|
|
493
493
|
/** @property {Number} - Red */
|
|
@@ -542,10 +542,10 @@ class Color
|
|
|
542
542
|
lerp(c, percent) { return this.add(c.subtract(this).scale(clamp(percent))); }
|
|
543
543
|
|
|
544
544
|
/** Sets this color given a hue, saturation, lightness, and alpha
|
|
545
|
-
* @param {Number} [h
|
|
546
|
-
* @param {Number} [s
|
|
547
|
-
* @param {Number} [l
|
|
548
|
-
* @param {Number} [a
|
|
545
|
+
* @param {Number} [h] - hue
|
|
546
|
+
* @param {Number} [s] - saturation
|
|
547
|
+
* @param {Number} [l] - lightness
|
|
548
|
+
* @param {Number} [a] - alpha
|
|
549
549
|
* @return {Color} */
|
|
550
550
|
setHSLA(h=0, s=0, l=1, a=1)
|
|
551
551
|
{
|
|
@@ -591,8 +591,8 @@ class Color
|
|
|
591
591
|
}
|
|
592
592
|
|
|
593
593
|
/** Returns a new color that has each component randomly adjusted
|
|
594
|
-
* @param {Number} [amount
|
|
595
|
-
* @param {Number} [alphaAmount
|
|
594
|
+
* @param {Number} [amount]
|
|
595
|
+
* @param {Number} [alphaAmount]
|
|
596
596
|
* @return {Color} */
|
|
597
597
|
mutate(amount=.05, alphaAmount=0)
|
|
598
598
|
{
|
|
@@ -606,9 +606,9 @@ class Color
|
|
|
606
606
|
}
|
|
607
607
|
|
|
608
608
|
/** Returns this color expressed as a hex color code
|
|
609
|
-
* @param {Boolean} [useAlpha
|
|
609
|
+
* @param {Boolean} [useAlpha] - if alpha should be included in result
|
|
610
610
|
* @return {String} */
|
|
611
|
-
toString(useAlpha =
|
|
611
|
+
toString(useAlpha = true)
|
|
612
612
|
{
|
|
613
613
|
const toHex = (c)=> ((c=c*255|0)<16 ? '0' : '') + c.toString(16);
|
|
614
614
|
return '#' + toHex(this.r) + toHex(this.g) + toHex(this.b) + (useAlpha ? toHex(this.a) : '');
|
|
@@ -657,7 +657,7 @@ class Timer
|
|
|
657
657
|
constructor(timeLeft) { this.time = timeLeft == undefined ? undefined : time + timeLeft; this.setTime = timeLeft; }
|
|
658
658
|
|
|
659
659
|
/** Set the timer with seconds passed in
|
|
660
|
-
* @param {Number} [timeLeft
|
|
660
|
+
* @param {Number} [timeLeft] - How much time left before the timer is elapsed in seconds */
|
|
661
661
|
set(timeLeft=0) { this.time = time + timeLeft; this.setTime = timeLeft; }
|
|
662
662
|
|
|
663
663
|
/** Unset the timer */
|
|
@@ -734,7 +734,7 @@ let canvasFixedSize = vec2();
|
|
|
734
734
|
* @type {Boolean}
|
|
735
735
|
* @default
|
|
736
736
|
* @memberof Settings */
|
|
737
|
-
let canvasPixelated =
|
|
737
|
+
let canvasPixelated = true;
|
|
738
738
|
|
|
739
739
|
/** Default font used for text rendering
|
|
740
740
|
* @type {String}
|
|
@@ -746,7 +746,7 @@ let fontDefault = 'arial';
|
|
|
746
746
|
* @type {Boolean}
|
|
747
747
|
* @default
|
|
748
748
|
* @memberof Settings */
|
|
749
|
-
let showSplashScreen =
|
|
749
|
+
let showSplashScreen = false;
|
|
750
750
|
|
|
751
751
|
///////////////////////////////////////////////////////////////////////////////
|
|
752
752
|
// WebGL settings
|
|
@@ -755,13 +755,13 @@ let showSplashScreen = 0;
|
|
|
755
755
|
* @type {Boolean}
|
|
756
756
|
* @default
|
|
757
757
|
* @memberof Settings */
|
|
758
|
-
let glEnable =
|
|
758
|
+
let glEnable = true;
|
|
759
759
|
|
|
760
760
|
/** Fixes slow rendering in some browsers by not compositing the WebGL canvas
|
|
761
761
|
* @type {Boolean}
|
|
762
762
|
* @default
|
|
763
763
|
* @memberof Settings */
|
|
764
|
-
let glOverlay =
|
|
764
|
+
let glOverlay = true;
|
|
765
765
|
|
|
766
766
|
///////////////////////////////////////////////////////////////////////////////
|
|
767
767
|
// Tile sheet settings
|
|
@@ -785,7 +785,7 @@ let tileFixBleedScale = .3;
|
|
|
785
785
|
* @type {Boolean}
|
|
786
786
|
* @default
|
|
787
787
|
* @memberof Settings */
|
|
788
|
-
let enablePhysicsSolver =
|
|
788
|
+
let enablePhysicsSolver = true;
|
|
789
789
|
|
|
790
790
|
/** Default object mass for collison calcuations (how heavy objects are)
|
|
791
791
|
* @type {Number}
|
|
@@ -807,7 +807,7 @@ let objectDefaultAngleDamping = 1;
|
|
|
807
807
|
|
|
808
808
|
/** How much to bounce when a collision occurs (0-1)
|
|
809
809
|
* @type {Number}
|
|
810
|
-
* @default
|
|
810
|
+
* @default
|
|
811
811
|
* @memberof Settings */
|
|
812
812
|
let objectDefaultElasticity = 0;
|
|
813
813
|
|
|
@@ -825,7 +825,7 @@ let objectMaxSpeed = 1;
|
|
|
825
825
|
|
|
826
826
|
/** How much gravity to apply to objects along the Y axis, negative is down
|
|
827
827
|
* @type {Number}
|
|
828
|
-
* @default
|
|
828
|
+
* @default
|
|
829
829
|
* @memberof Settings */
|
|
830
830
|
let gravity = 0;
|
|
831
831
|
|
|
@@ -842,33 +842,33 @@ let particleEmitRateScale = 1;
|
|
|
842
842
|
* @type {Boolean}
|
|
843
843
|
* @default
|
|
844
844
|
* @memberof Settings */
|
|
845
|
-
let gamepadsEnable =
|
|
845
|
+
let gamepadsEnable = true;
|
|
846
846
|
|
|
847
847
|
/** If true, the dpad input is also routed to the left analog stick (for better accessability)
|
|
848
848
|
* @type {Boolean}
|
|
849
849
|
* @default
|
|
850
850
|
* @memberof Settings */
|
|
851
|
-
let gamepadDirectionEmulateStick =
|
|
851
|
+
let gamepadDirectionEmulateStick = true;
|
|
852
852
|
|
|
853
853
|
/** If true the WASD keys are also routed to the direction keys (for better accessability)
|
|
854
854
|
* @type {Boolean}
|
|
855
855
|
* @default
|
|
856
856
|
* @memberof Settings */
|
|
857
|
-
let inputWASDEmulateDirection =
|
|
857
|
+
let inputWASDEmulateDirection = true;
|
|
858
858
|
|
|
859
859
|
/** True if touch gamepad should appear on mobile devices
|
|
860
860
|
* - Supports left analog stick, 4 face buttons and start button (button 9)
|
|
861
861
|
* - Must be set by end of gameInit to be activated
|
|
862
862
|
* @type {Boolean}
|
|
863
|
-
* @default
|
|
863
|
+
* @default
|
|
864
864
|
* @memberof Settings */
|
|
865
|
-
let touchGamepadEnable =
|
|
865
|
+
let touchGamepadEnable = false;
|
|
866
866
|
|
|
867
867
|
/** True if touch gamepad should be analog stick or false to use if 8 way dpad
|
|
868
868
|
* @type {Boolean}
|
|
869
869
|
* @default
|
|
870
870
|
* @memberof Settings */
|
|
871
|
-
let touchGamepadAnalog =
|
|
871
|
+
let touchGamepadAnalog = true;
|
|
872
872
|
|
|
873
873
|
/** Size of virutal gamepad for touch devices in pixels
|
|
874
874
|
* @type {Number}
|
|
@@ -886,7 +886,7 @@ let touchGamepadAlpha = .3;
|
|
|
886
886
|
* @type {Boolean}
|
|
887
887
|
* @default
|
|
888
888
|
* @memberof Settings */
|
|
889
|
-
let vibrateEnable =
|
|
889
|
+
let vibrateEnable = true;
|
|
890
890
|
|
|
891
891
|
///////////////////////////////////////////////////////////////////////////////
|
|
892
892
|
// Audio settings
|
|
@@ -895,7 +895,7 @@ let vibrateEnable = 1;
|
|
|
895
895
|
* @type {Boolean}
|
|
896
896
|
* @default
|
|
897
897
|
* @memberof Settings */
|
|
898
|
-
let soundEnable =
|
|
898
|
+
let soundEnable = true;
|
|
899
899
|
|
|
900
900
|
/** Volume scale to apply to all sound, music and speech
|
|
901
901
|
* @type {Number}
|
|
@@ -944,9 +944,9 @@ let medalDisplayIconSize = 50;
|
|
|
944
944
|
|
|
945
945
|
/** Set to stop medals from being unlockable (like if cheats are enabled)
|
|
946
946
|
* @type {Boolean}
|
|
947
|
-
* @default
|
|
947
|
+
* @default
|
|
948
948
|
* @memberof Settings */
|
|
949
|
-
let medalsPreventUnlock;
|
|
949
|
+
let medalsPreventUnlock = false;
|
|
950
950
|
|
|
951
951
|
///////////////////////////////////////////////////////////////////////////////
|
|
952
952
|
// Setters for global variables
|
|
@@ -1017,12 +1017,12 @@ function setEnablePhysicsSolver(enable) { enablePhysicsSolver = enable; }
|
|
|
1017
1017
|
function setObjectDefaultMass(mass) { objectDefaultMass = mass; }
|
|
1018
1018
|
|
|
1019
1019
|
/** Set how much to slow velocity by each frame
|
|
1020
|
-
* @param {Number}
|
|
1020
|
+
* @param {Number} damp
|
|
1021
1021
|
* @memberof Settings */
|
|
1022
1022
|
function setObjectDefaultDamping(damp) { objectDefaultDamping = damp; }
|
|
1023
1023
|
|
|
1024
1024
|
/** Set how much to slow angular velocity each frame
|
|
1025
|
-
* @param {Number}
|
|
1025
|
+
* @param {Number} damp
|
|
1026
1026
|
* @memberof Settings */
|
|
1027
1027
|
function setObjectDefaultAngleDamping(damp) { objectDefaultAngleDamping = damp; }
|
|
1028
1028
|
|
|
@@ -1042,9 +1042,9 @@ function setObjectDefaultFriction(friction) { objectDefaultFriction = friction;
|
|
|
1042
1042
|
function setObjectMaxSpeed(speed) { objectMaxSpeed = speed; }
|
|
1043
1043
|
|
|
1044
1044
|
/** Set how much gravity to apply to objects along the Y axis
|
|
1045
|
-
* @param {Number}
|
|
1045
|
+
* @param {Number} newGravity
|
|
1046
1046
|
* @memberof Settings */
|
|
1047
|
-
function setGravity(
|
|
1047
|
+
function setGravity(newGravity) { gravity = newGravity; }
|
|
1048
1048
|
|
|
1049
1049
|
/** Set to scales emit rate of particles
|
|
1050
1050
|
* @param {Number} scale
|
|
@@ -1142,7 +1142,7 @@ function setMedalsPreventUnlock(preventUnlock) { medalsPreventUnlock = preventUn
|
|
|
1142
1142
|
function setShowWatermark(show) { showWatermark = show; }
|
|
1143
1143
|
|
|
1144
1144
|
/** Set key code used to toggle debug mode, Esc by default
|
|
1145
|
-
* @param {
|
|
1145
|
+
* @param {String} key
|
|
1146
1146
|
* @memberof Debug */
|
|
1147
1147
|
function setDebugKey(key) { debugKey = key; }
|
|
1148
1148
|
/**
|
|
@@ -1179,27 +1179,25 @@ function setDebugKey(key) { debugKey = key; }
|
|
|
1179
1179
|
class EngineObject
|
|
1180
1180
|
{
|
|
1181
1181
|
/** Create an engine object and adds it to the list of objects
|
|
1182
|
-
* @param {Vector2}
|
|
1183
|
-
* @param {Vector2}
|
|
1184
|
-
* @param {TileInfo} [tileInfo]
|
|
1185
|
-
* @param {Number}
|
|
1186
|
-
* @param {Color}
|
|
1187
|
-
* @param {Number}
|
|
1182
|
+
* @param {Vector2} [pos=Vector2()] - World space position of the object
|
|
1183
|
+
* @param {Vector2} [size=Vector2(1,1)] - World space size of the object
|
|
1184
|
+
* @param {TileInfo} [tileInfo] - Tile info to render object (undefined is untextured)
|
|
1185
|
+
* @param {Number} [angle] - Angle the object is rotated by
|
|
1186
|
+
* @param {Color} [color=Color()] - Color to apply to tile when rendered
|
|
1187
|
+
* @param {Number} [renderOrder] - Objects sorted by renderOrder before being rendered
|
|
1188
1188
|
*/
|
|
1189
1189
|
constructor(pos=vec2(), size=vec2(1), tileInfo, angle=0, color, renderOrder=0)
|
|
1190
1190
|
{
|
|
1191
1191
|
// set passed in params
|
|
1192
|
-
ASSERT(isVector2(pos) && isVector2(size)
|
|
1193
|
-
ASSERT(typeof tileInfo !== 'number' || !tileInfo
|
|
1194
|
-
ASSERT(!(renderOrder instanceof Color)); // prevent old style calls
|
|
1195
|
-
// to fix old calls, replace with tile(tileIndex, tileSize)
|
|
1192
|
+
ASSERT(isVector2(pos) && isVector2(size), 'ensure pos and size are vec2s');
|
|
1193
|
+
ASSERT(typeof tileInfo !== 'number' || !tileInfo, 'old style tile setup');
|
|
1196
1194
|
|
|
1197
1195
|
/** @property {Vector2} - World space position of the object */
|
|
1198
1196
|
this.pos = pos.copy();
|
|
1199
1197
|
/** @property {Vector2} - World space width and height of the object */
|
|
1200
1198
|
this.size = size;
|
|
1201
1199
|
/** @property {Vector2} - Size of object used for drawing, uses size if not set */
|
|
1202
|
-
this.drawSize;
|
|
1200
|
+
this.drawSize = undefined;
|
|
1203
1201
|
/** @property {TileInfo} - Tile info to render object (undefined is untextured) */
|
|
1204
1202
|
this.tileInfo = tileInfo;
|
|
1205
1203
|
/** @property {Number} - Angle to rotate the object */
|
|
@@ -1207,9 +1205,11 @@ class EngineObject
|
|
|
1207
1205
|
/** @property {Color} - Color to apply when rendered */
|
|
1208
1206
|
this.color = color;
|
|
1209
1207
|
/** @property {Color} - Additive color to apply when rendered */
|
|
1210
|
-
this.additiveColor;
|
|
1208
|
+
this.additiveColor = undefined;
|
|
1209
|
+
/** @property {Boolean} - Should it flip along y axis when rendered */
|
|
1210
|
+
this.mirror = false;
|
|
1211
1211
|
|
|
1212
|
-
//
|
|
1212
|
+
// physical properties
|
|
1213
1213
|
/** @property {Number} [mass=objectDefaultMass] - How heavy the object is, static if 0 */
|
|
1214
1214
|
this.mass = objectDefaultMass;
|
|
1215
1215
|
/** @property {Number} [damping=objectDefaultDamping] - How much to slow down velocity each frame (0-1) */
|
|
@@ -1220,19 +1220,34 @@ class EngineObject
|
|
|
1220
1220
|
this.elasticity = objectDefaultElasticity;
|
|
1221
1221
|
/** @property {Number} [friction=objectDefaultFriction] - How much friction to apply when sliding (0-1) */
|
|
1222
1222
|
this.friction = objectDefaultFriction;
|
|
1223
|
-
/** @property {Number}
|
|
1223
|
+
/** @property {Number} - How much to scale gravity by for this object */
|
|
1224
1224
|
this.gravityScale = 1;
|
|
1225
|
-
/** @property {Number}
|
|
1225
|
+
/** @property {Number} - Objects are sorted by render order */
|
|
1226
1226
|
this.renderOrder = renderOrder;
|
|
1227
|
-
/** @property {Vector2}
|
|
1227
|
+
/** @property {Vector2} - Velocity of the object */
|
|
1228
1228
|
this.velocity = vec2();
|
|
1229
|
-
/** @property {Number}
|
|
1229
|
+
/** @property {Number} - Angular velocity of the object */
|
|
1230
1230
|
this.angleVelocity = 0;
|
|
1231
|
-
|
|
1232
|
-
// init other internal object stuff
|
|
1231
|
+
/** @property {Number} - Track when object was created */
|
|
1233
1232
|
this.spawnTime = time;
|
|
1233
|
+
/** @property {Array} - List of children of this object */
|
|
1234
1234
|
this.children = [];
|
|
1235
|
+
|
|
1236
|
+
// parent child system
|
|
1237
|
+
/** @property {EngineObject} - Parent of object if in local space */
|
|
1238
|
+
this.parent = undefined;
|
|
1239
|
+
/** @property {Vector2} - Local position if child */
|
|
1240
|
+
this.localPos = vec2();
|
|
1241
|
+
/** @property {Number} - Local angle if child */
|
|
1242
|
+
this.localAngle = 0;
|
|
1243
|
+
|
|
1244
|
+
// collision flags
|
|
1245
|
+
/** @property {Boolean} - Object collides with the tile collision */
|
|
1235
1246
|
this.collideTiles = false;
|
|
1247
|
+
/** @property {Boolean} - Object collides with solid objects */
|
|
1248
|
+
this.collideSolidObjects = false;
|
|
1249
|
+
/** @property {Boolean} - Object collides with and blocks other objects */
|
|
1250
|
+
this.isSolid = false;
|
|
1236
1251
|
|
|
1237
1252
|
// add to list of objects
|
|
1238
1253
|
engineObjects.push(this);
|
|
@@ -1449,7 +1464,7 @@ class EngineObject
|
|
|
1449
1464
|
* @param {EngineObject} object - the object to test against
|
|
1450
1465
|
* @return {Boolean} - true if the collision should be resolved
|
|
1451
1466
|
*/
|
|
1452
|
-
collideWithObject(object) { return
|
|
1467
|
+
collideWithObject(object) { return true; }
|
|
1453
1468
|
|
|
1454
1469
|
/** How long since the object was created
|
|
1455
1470
|
* @return {Number} */
|
|
@@ -1470,7 +1485,7 @@ class EngineObject
|
|
|
1470
1485
|
/** Attaches a child to this with a given local transform
|
|
1471
1486
|
* @param {EngineObject} child
|
|
1472
1487
|
* @param {Vector2} [localPos=Vector2()]
|
|
1473
|
-
* @param {Number} [localAngle
|
|
1488
|
+
* @param {Number} [localAngle] */
|
|
1474
1489
|
addChild(child, localPos=vec2(), localAngle=0)
|
|
1475
1490
|
{
|
|
1476
1491
|
ASSERT(!child.parent && !this.children.includes(child));
|
|
@@ -1490,12 +1505,12 @@ class EngineObject
|
|
|
1490
1505
|
}
|
|
1491
1506
|
|
|
1492
1507
|
/** Set how this object collides
|
|
1493
|
-
* @param {Boolean} [collideSolidObjects
|
|
1494
|
-
* @param {Boolean} [isSolid
|
|
1495
|
-
* @param {Boolean} [collideTiles
|
|
1496
|
-
setCollision(collideSolidObjects=
|
|
1508
|
+
* @param {Boolean} [collideSolidObjects] - Does it collide with solid objects
|
|
1509
|
+
* @param {Boolean} [isSolid] - Does it collide with and block other objects (expensive in large numbers)
|
|
1510
|
+
* @param {Boolean} [collideTiles] - Does it collide with the tile collision */
|
|
1511
|
+
setCollision(collideSolidObjects=true, isSolid=true, collideTiles=true)
|
|
1497
1512
|
{
|
|
1498
|
-
ASSERT(collideSolidObjects || !isSolid
|
|
1513
|
+
ASSERT(collideSolidObjects || !isSolid, 'solid objects must be set to collide');
|
|
1499
1514
|
|
|
1500
1515
|
this.collideSolidObjects = collideSolidObjects;
|
|
1501
1516
|
this.isSolid = isSolid;
|
|
@@ -1586,9 +1601,9 @@ let drawCount;
|
|
|
1586
1601
|
* Create a tile info object
|
|
1587
1602
|
* - This can take vecs or floats for easier use and conversion
|
|
1588
1603
|
* - If an index is passed in, the tile size and index will determine the position
|
|
1589
|
-
* @param {(Number|Vector2)} [pos=Vector2()]
|
|
1590
|
-
* @param {(Number|Vector2)} [size=tileSizeDefault]
|
|
1591
|
-
* @param {Number} [textureIndex
|
|
1604
|
+
* @param {(Number|Vector2)} [pos=Vector2()] - Top left corner of tile in pixels or index
|
|
1605
|
+
* @param {(Number|Vector2)} [size=tileSizeDefault] - Size of tile in pixels
|
|
1606
|
+
* @param {Number} [textureIndex] - Texture index to use
|
|
1592
1607
|
* @return {TileInfo}
|
|
1593
1608
|
* @example
|
|
1594
1609
|
* tile(2) // a tile at index 2 using the default tile size of 16
|
|
@@ -1600,14 +1615,14 @@ let drawCount;
|
|
|
1600
1615
|
function tile(pos=vec2(), size=tileSizeDefault, textureIndex=0)
|
|
1601
1616
|
{
|
|
1602
1617
|
// if size is a number, make it a vector
|
|
1603
|
-
if (size
|
|
1618
|
+
if (typeof size === 'number')
|
|
1604
1619
|
{
|
|
1605
1620
|
ASSERT(size > 0);
|
|
1606
1621
|
size = vec2(size);
|
|
1607
1622
|
}
|
|
1608
1623
|
|
|
1609
1624
|
// if pos is a number, use it as a tile index
|
|
1610
|
-
if (pos
|
|
1625
|
+
if (typeof pos === 'number')
|
|
1611
1626
|
{
|
|
1612
1627
|
const textureInfo = textureInfos[textureIndex];
|
|
1613
1628
|
if (textureInfo)
|
|
@@ -1631,7 +1646,7 @@ class TileInfo
|
|
|
1631
1646
|
/** Create a tile info object
|
|
1632
1647
|
* @param {Vector2} [pos=Vector2()] - Top left corner of tile in pixels
|
|
1633
1648
|
* @param {Vector2} [size=tileSizeDefault] - Size of tile in pixels
|
|
1634
|
-
* @param {Number} [textureIndex
|
|
1649
|
+
* @param {Number} [textureIndex] - Texture index to use
|
|
1635
1650
|
*/
|
|
1636
1651
|
constructor(pos=vec2(), size=tileSizeDefault, textureIndex=0)
|
|
1637
1652
|
{
|
|
@@ -1660,10 +1675,13 @@ class TileInfo
|
|
|
1660
1675
|
/** Texture Info - Stores info about each texture */
|
|
1661
1676
|
class TextureInfo
|
|
1662
1677
|
{
|
|
1663
|
-
|
|
1678
|
+
/**
|
|
1679
|
+
* Create a TextureInfo, called automatically by the engine
|
|
1680
|
+
* @param {HTMLImageElement} image
|
|
1681
|
+
*/
|
|
1664
1682
|
constructor(image)
|
|
1665
1683
|
{
|
|
1666
|
-
/** @property {
|
|
1684
|
+
/** @property {HTMLImageElement} - image source */
|
|
1667
1685
|
this.image = image;
|
|
1668
1686
|
/** @property {Vector2} - size of the image */
|
|
1669
1687
|
this.size = vec2(image.width, image.height);
|
|
@@ -1706,23 +1724,21 @@ function worldToScreen(worldPos)
|
|
|
1706
1724
|
* @param {Vector2} pos - Center of the tile in world space
|
|
1707
1725
|
* @param {Vector2} [size=Vector2(1,1)] - Size of the tile in world space
|
|
1708
1726
|
* @param {TileInfo}[tileInfo] - Tile info to use, untextured if undefined
|
|
1709
|
-
* @param {Vector2} [tileSize=tileSizeDefault] - Tile size in source pixels
|
|
1710
1727
|
* @param {Color} [color=Color()] - Color to modulate with
|
|
1711
|
-
* @param {Number} [angle
|
|
1712
|
-
* @param {Boolean} [mirror
|
|
1728
|
+
* @param {Number} [angle] - Angle to rotate by
|
|
1729
|
+
* @param {Boolean} [mirror] - If true image is flipped along the Y axis
|
|
1713
1730
|
* @param {Color} [additiveColor=Color(0,0,0,0)] - Additive color to be applied
|
|
1714
1731
|
* @param {Boolean} [useWebGL=glEnable] - Use accelerated WebGL rendering
|
|
1715
|
-
* @param {Boolean} [screenSpace
|
|
1732
|
+
* @param {Boolean} [screenSpace] - If true the pos and size are in screen space
|
|
1716
1733
|
* @param {CanvasRenderingContext2D} [context] - Canvas 2D context to draw to
|
|
1717
1734
|
* @memberof Draw */
|
|
1718
1735
|
function drawTile(pos, size=vec2(1), tileInfo, color=new Color,
|
|
1719
1736
|
angle=0, mirror, additiveColor=new Color(0,0,0,0), useWebGL=glEnable, screenSpace, context)
|
|
1720
1737
|
{
|
|
1721
|
-
ASSERT(!context || !useWebGL
|
|
1722
|
-
ASSERT(typeof tileInfo !== 'number' || !tileInfo
|
|
1723
|
-
|
|
1738
|
+
ASSERT(!context || !useWebGL, 'context only supported in canvas 2D mode');
|
|
1739
|
+
ASSERT(typeof tileInfo !== 'number' || !tileInfo,
|
|
1740
|
+
'this is an old style calls, to fix replace it with tile(tileIndex, tileSize)');
|
|
1724
1741
|
|
|
1725
|
-
showWatermark && ++drawCount;
|
|
1726
1742
|
const textureInfo = tileInfo && tileInfo.getTextureInfo();
|
|
1727
1743
|
if (useWebGL)
|
|
1728
1744
|
{
|
|
@@ -1756,6 +1772,7 @@ function drawTile(pos, size=vec2(1), tileInfo, color=new Color,
|
|
|
1756
1772
|
else
|
|
1757
1773
|
{
|
|
1758
1774
|
// normal canvas 2D rendering method (slower)
|
|
1775
|
+
showWatermark && ++drawCount;
|
|
1759
1776
|
drawCanvas2D(pos, size, angle, mirror, (context)=>
|
|
1760
1777
|
{
|
|
1761
1778
|
if (textureInfo)
|
|
@@ -1783,49 +1800,38 @@ function drawTile(pos, size=vec2(1), tileInfo, color=new Color,
|
|
|
1783
1800
|
* @param {Vector2} pos
|
|
1784
1801
|
* @param {Vector2} [size=Vector2(1,1)]
|
|
1785
1802
|
* @param {Color} [color=Color()]
|
|
1786
|
-
* @param {Number} [angle
|
|
1803
|
+
* @param {Number} [angle]
|
|
1787
1804
|
* @param {Boolean} [useWebGL=glEnable]
|
|
1788
|
-
* @param {Boolean} [screenSpace
|
|
1805
|
+
* @param {Boolean} [screenSpace]
|
|
1789
1806
|
* @param {CanvasRenderingContext2D} [context]
|
|
1790
1807
|
* @memberof Draw */
|
|
1791
1808
|
function drawRect(pos, size, color, angle, useWebGL, screenSpace, context)
|
|
1792
1809
|
{
|
|
1793
|
-
drawTile(pos, size, undefined, color, angle,
|
|
1810
|
+
drawTile(pos, size, undefined, color, angle, false, undefined, useWebGL, screenSpace, context);
|
|
1794
1811
|
}
|
|
1795
1812
|
|
|
1796
1813
|
/** Draw colored polygon using passed in points
|
|
1797
1814
|
* @param {Array} points - Array of Vector2 points
|
|
1798
1815
|
* @param {Color} [color=Color()]
|
|
1799
|
-
* @param {Boolean} [
|
|
1800
|
-
* @param {
|
|
1801
|
-
* @param {CanvasRenderingContext2D} [context]
|
|
1816
|
+
* @param {Boolean} [screenSpace]
|
|
1817
|
+
* @param {CanvasRenderingContext2D} [context=mainContext]
|
|
1802
1818
|
* @memberof Draw */
|
|
1803
|
-
function drawPoly(points, color=new Color,
|
|
1819
|
+
function drawPoly(points, color=new Color, screenSpace, context=mainContext)
|
|
1804
1820
|
{
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
{
|
|
1811
|
-
// draw using canvas
|
|
1812
|
-
if (!context)
|
|
1813
|
-
context = mainContext;
|
|
1814
|
-
context.fillStyle = color;
|
|
1815
|
-
context.beginPath();
|
|
1816
|
-
for (const point of screenSpace ? points : points.map(worldToScreen))
|
|
1817
|
-
context.lineTo(point.x, point.y);
|
|
1818
|
-
context.fill();
|
|
1819
|
-
}
|
|
1821
|
+
context.fillStyle = color.toString();
|
|
1822
|
+
context.beginPath();
|
|
1823
|
+
for (const point of screenSpace ? points : points.map(worldToScreen))
|
|
1824
|
+
context.lineTo(point.x, point.y);
|
|
1825
|
+
context.fill();
|
|
1820
1826
|
}
|
|
1821
1827
|
|
|
1822
1828
|
/** Draw colored line between two points
|
|
1823
1829
|
* @param {Vector2} posA
|
|
1824
1830
|
* @param {Vector2} posB
|
|
1825
|
-
* @param {Number} [thickness
|
|
1831
|
+
* @param {Number} [thickness]
|
|
1826
1832
|
* @param {Color} [color=Color()]
|
|
1827
1833
|
* @param {Boolean} [useWebGL=glEnable]
|
|
1828
|
-
* @param {Boolean} [screenSpace
|
|
1834
|
+
* @param {Boolean} [screenSpace]
|
|
1829
1835
|
* @param {CanvasRenderingContext2D} [context]
|
|
1830
1836
|
* @memberof Draw */
|
|
1831
1837
|
function drawLine(posA, posB, thickness=.1, color, useWebGL, screenSpace, context)
|
|
@@ -1841,7 +1847,7 @@ function drawLine(posA, posB, thickness=.1, color, useWebGL, screenSpace, contex
|
|
|
1841
1847
|
* @param {Number} angle
|
|
1842
1848
|
* @param {Boolean} mirror
|
|
1843
1849
|
* @param {Function} drawFunction
|
|
1844
|
-
* @param {Boolean} [screenSpace
|
|
1850
|
+
* @param {Boolean} [screenSpace]
|
|
1845
1851
|
* @param {CanvasRenderingContext2D} [context=mainContext]
|
|
1846
1852
|
* @memberof Draw */
|
|
1847
1853
|
function drawCanvas2D(pos, size, angle, mirror, drawFunction, screenSpace, context=mainContext)
|
|
@@ -1861,13 +1867,13 @@ function drawCanvas2D(pos, size, angle, mirror, drawFunction, screenSpace, conte
|
|
|
1861
1867
|
}
|
|
1862
1868
|
|
|
1863
1869
|
/** Enable normal or additive blend mode
|
|
1864
|
-
* @param {Boolean} [additive
|
|
1870
|
+
* @param {Boolean} [additive]
|
|
1865
1871
|
* @param {Boolean} [useWebGL=glEnable]
|
|
1866
1872
|
* @param {CanvasRenderingContext2D} [context=mainContext]
|
|
1867
1873
|
* @memberof Draw */
|
|
1868
1874
|
function setBlendMode(additive, useWebGL=glEnable, context)
|
|
1869
1875
|
{
|
|
1870
|
-
ASSERT(!context || !useWebGL
|
|
1876
|
+
ASSERT(!context || !useWebGL, 'context only supported in canvas 2D mode');
|
|
1871
1877
|
if (useWebGL)
|
|
1872
1878
|
glAdditive = additive;
|
|
1873
1879
|
else
|
|
@@ -1882,11 +1888,11 @@ function setBlendMode(additive, useWebGL=glEnable, context)
|
|
|
1882
1888
|
* Automatically splits new lines into rows
|
|
1883
1889
|
* @param {String} text
|
|
1884
1890
|
* @param {Vector2} pos
|
|
1885
|
-
* @param {Number} [size
|
|
1891
|
+
* @param {Number} [size]
|
|
1886
1892
|
* @param {Color} [color=Color()]
|
|
1887
|
-
* @param {Number} [lineWidth
|
|
1893
|
+
* @param {Number} [lineWidth]
|
|
1888
1894
|
* @param {Color} [lineColor=Color(0,0,0)]
|
|
1889
|
-
* @param {
|
|
1895
|
+
* @param {CanvasTextAlign} [textAlign='center']
|
|
1890
1896
|
* @param {String} [font=fontDefault]
|
|
1891
1897
|
* @param {CanvasRenderingContext2D} [context=overlayContext]
|
|
1892
1898
|
* @memberof Draw */
|
|
@@ -1899,19 +1905,19 @@ function drawText(text, pos, size=1, color, lineWidth=0, lineColor, textAlign, f
|
|
|
1899
1905
|
* Automatically splits new lines into rows
|
|
1900
1906
|
* @param {String} text
|
|
1901
1907
|
* @param {Vector2} pos
|
|
1902
|
-
* @param {Number} [size
|
|
1908
|
+
* @param {Number} [size]
|
|
1903
1909
|
* @param {Color} [color=Color()]
|
|
1904
|
-
* @param {Number} [lineWidth
|
|
1910
|
+
* @param {Number} [lineWidth]
|
|
1905
1911
|
* @param {Color} [lineColor=Color(0,0,0)]
|
|
1906
|
-
* @param {
|
|
1912
|
+
* @param {CanvasTextAlign} [textAlign]
|
|
1907
1913
|
* @param {String} [font=fontDefault]
|
|
1908
1914
|
* @param {CanvasRenderingContext2D} [context=overlayContext]
|
|
1909
1915
|
* @memberof Draw */
|
|
1910
1916
|
function drawTextScreen(text, pos, size=1, color=new Color, lineWidth=0, lineColor=new Color(0,0,0), textAlign='center', font=fontDefault, context=overlayContext)
|
|
1911
1917
|
{
|
|
1912
|
-
context.fillStyle = color;
|
|
1918
|
+
context.fillStyle = color.toString();
|
|
1913
1919
|
context.lineWidth = lineWidth;
|
|
1914
|
-
context.strokeStyle = lineColor;
|
|
1920
|
+
context.strokeStyle = lineColor.toString();
|
|
1915
1921
|
context.textAlign = textAlign;
|
|
1916
1922
|
context.font = size + 'px '+ font;
|
|
1917
1923
|
context.textBaseline = 'middle';
|
|
@@ -1946,8 +1952,8 @@ class FontImage
|
|
|
1946
1952
|
{
|
|
1947
1953
|
/** Create an image font
|
|
1948
1954
|
* @param {HTMLImageElement} [image] - Image for the font, if undefined default font is used
|
|
1949
|
-
* @param {Vector2} [tileSize=
|
|
1950
|
-
* @param {Vector2} [paddingSize=
|
|
1955
|
+
* @param {Vector2} [tileSize=Vector2(8)] - Size of the font source tiles
|
|
1956
|
+
* @param {Vector2} [paddingSize=Vector2(0,1)] - How much extra space to add between characters
|
|
1951
1957
|
* @param {CanvasRenderingContext2D} [context=overlayContext] - context to draw to
|
|
1952
1958
|
*/
|
|
1953
1959
|
constructor(image, tileSize=vec2(8), paddingSize=vec2(0,1), context=overlayContext)
|
|
@@ -1976,7 +1982,7 @@ class FontImage
|
|
|
1976
1982
|
/** Draw text in screen space using the image font
|
|
1977
1983
|
* @param {String} text
|
|
1978
1984
|
* @param {Vector2} pos
|
|
1979
|
-
* @param {Number} [scale
|
|
1985
|
+
* @param {Number} [scale]
|
|
1980
1986
|
* @param {Boolean} [center]
|
|
1981
1987
|
*/
|
|
1982
1988
|
drawTextScreen(text, pos, scale=4, center)
|
|
@@ -1994,7 +2000,7 @@ class FontImage
|
|
|
1994
2000
|
for(let j=line.length; j--;)
|
|
1995
2001
|
{
|
|
1996
2002
|
// draw each character
|
|
1997
|
-
let charCode = line[j].charCodeAt();
|
|
2003
|
+
let charCode = line[j].charCodeAt(0);
|
|
1998
2004
|
if (charCode < 32 || charCode > 127)
|
|
1999
2005
|
charCode = 127; // unknown character
|
|
2000
2006
|
|
|
@@ -2018,7 +2024,7 @@ class FontImage
|
|
|
2018
2024
|
/** Returns true if fullscreen mode is active
|
|
2019
2025
|
* @return {Boolean}
|
|
2020
2026
|
* @memberof Draw */
|
|
2021
|
-
function isFullscreen() { return document.fullscreenElement; }
|
|
2027
|
+
function isFullscreen() { return !!document.fullscreenElement; }
|
|
2022
2028
|
|
|
2023
2029
|
/** Toggle fullsceen mode
|
|
2024
2030
|
* @memberof Draw */
|
|
@@ -2044,28 +2050,37 @@ function toggleFullscreen()
|
|
|
2044
2050
|
|
|
2045
2051
|
|
|
2046
2052
|
/** Returns true if device key is down
|
|
2047
|
-
* @param {Number} key
|
|
2048
|
-
* @param {Number} [device
|
|
2053
|
+
* @param {String|Number} key
|
|
2054
|
+
* @param {Number} [device]
|
|
2049
2055
|
* @return {Boolean}
|
|
2050
2056
|
* @memberof Input */
|
|
2051
2057
|
function keyIsDown(key, device=0)
|
|
2052
|
-
{
|
|
2058
|
+
{
|
|
2059
|
+
ASSERT(device > 0 || typeof key !== 'number' || key < 3, 'use code string for keyboard');
|
|
2060
|
+
return inputData[device] && !!(inputData[device][key] & 1);
|
|
2061
|
+
}
|
|
2053
2062
|
|
|
2054
2063
|
/** Returns true if device key was pressed this frame
|
|
2055
|
-
* @param {Number} key
|
|
2056
|
-
* @param {Number} [device
|
|
2064
|
+
* @param {String|Number} key
|
|
2065
|
+
* @param {Number} [device]
|
|
2057
2066
|
* @return {Boolean}
|
|
2058
2067
|
* @memberof Input */
|
|
2059
2068
|
function keyWasPressed(key, device=0)
|
|
2060
|
-
{
|
|
2069
|
+
{
|
|
2070
|
+
ASSERT(device > 0 || typeof key !== 'number' || key < 3, 'use code string for keyboard');
|
|
2071
|
+
return inputData[device] && !!(inputData[device][key] & 2);
|
|
2072
|
+
}
|
|
2061
2073
|
|
|
2062
2074
|
/** Returns true if device key was released this frame
|
|
2063
|
-
* @param {Number} key
|
|
2064
|
-
* @param {Number} [device
|
|
2075
|
+
* @param {String|Number} key
|
|
2076
|
+
* @param {Number} [device]
|
|
2065
2077
|
* @return {Boolean}
|
|
2066
2078
|
* @memberof Input */
|
|
2067
2079
|
function keyWasReleased(key, device=0)
|
|
2068
|
-
{
|
|
2080
|
+
{
|
|
2081
|
+
ASSERT(device > 0 || typeof key !== 'number' || key < 3, 'use code string for keyboard');
|
|
2082
|
+
return inputData[device] && !!(inputData[device][key] & 4);
|
|
2083
|
+
}
|
|
2069
2084
|
|
|
2070
2085
|
/** Clears all input
|
|
2071
2086
|
* @memberof Input */
|
|
@@ -2110,16 +2125,16 @@ let mouseWheel = 0;
|
|
|
2110
2125
|
/** Returns true if user is using gamepad (has more recently pressed a gamepad button)
|
|
2111
2126
|
* @type {Boolean}
|
|
2112
2127
|
* @memberof Input */
|
|
2113
|
-
let isUsingGamepad =
|
|
2128
|
+
let isUsingGamepad = false;
|
|
2114
2129
|
|
|
2115
2130
|
/** Prevents input continuing to the default browser handling (false by default)
|
|
2116
2131
|
* @type {Boolean}
|
|
2117
2132
|
* @memberof Input */
|
|
2118
|
-
let preventDefaultInput =
|
|
2133
|
+
let preventDefaultInput = false;
|
|
2119
2134
|
|
|
2120
2135
|
/** Returns true if gamepad button is down
|
|
2121
2136
|
* @param {Number} button
|
|
2122
|
-
* @param {Number} [gamepad
|
|
2137
|
+
* @param {Number} [gamepad]
|
|
2123
2138
|
* @return {Boolean}
|
|
2124
2139
|
* @memberof Input */
|
|
2125
2140
|
function gamepadIsDown(button, gamepad=0)
|
|
@@ -2127,7 +2142,7 @@ function gamepadIsDown(button, gamepad=0)
|
|
|
2127
2142
|
|
|
2128
2143
|
/** Returns true if gamepad button was pressed
|
|
2129
2144
|
* @param {Number} button
|
|
2130
|
-
* @param {Number} [gamepad
|
|
2145
|
+
* @param {Number} [gamepad]
|
|
2131
2146
|
* @return {Boolean}
|
|
2132
2147
|
* @memberof Input */
|
|
2133
2148
|
function gamepadWasPressed(button, gamepad=0)
|
|
@@ -2135,7 +2150,7 @@ function gamepadWasPressed(button, gamepad=0)
|
|
|
2135
2150
|
|
|
2136
2151
|
/** Returns true if gamepad button was released
|
|
2137
2152
|
* @param {Number} button
|
|
2138
|
-
* @param {Number} [gamepad
|
|
2153
|
+
* @param {Number} [gamepad]
|
|
2139
2154
|
* @return {Boolean}
|
|
2140
2155
|
* @memberof Input */
|
|
2141
2156
|
function gamepadWasReleased(button, gamepad=0)
|
|
@@ -2143,7 +2158,7 @@ function gamepadWasReleased(button, gamepad=0)
|
|
|
2143
2158
|
|
|
2144
2159
|
/** Returns gamepad stick value
|
|
2145
2160
|
* @param {Number} stick
|
|
2146
|
-
* @param {Number} [gamepad
|
|
2161
|
+
* @param {Number} [gamepad]
|
|
2147
2162
|
* @return {Vector2}
|
|
2148
2163
|
* @memberof Input */
|
|
2149
2164
|
function gamepadStick(stick, gamepad=0)
|
|
@@ -2186,9 +2201,10 @@ function inputUpdatePost()
|
|
|
2186
2201
|
if (debug && e.target != document.body) return;
|
|
2187
2202
|
if (!e.repeat)
|
|
2188
2203
|
{
|
|
2189
|
-
|
|
2204
|
+
isUsingGamepad = false;
|
|
2205
|
+
inputData[0][e.code] = 3;
|
|
2190
2206
|
if (inputWASDEmulateDirection)
|
|
2191
|
-
inputData[0][remapKey(e.
|
|
2207
|
+
inputData[0][remapKey(e.code)] = 3;
|
|
2192
2208
|
}
|
|
2193
2209
|
preventDefaultInput && e.preventDefault();
|
|
2194
2210
|
}
|
|
@@ -2196,26 +2212,29 @@ function inputUpdatePost()
|
|
|
2196
2212
|
onkeyup = (e)=>
|
|
2197
2213
|
{
|
|
2198
2214
|
if (debug && e.target != document.body) return;
|
|
2199
|
-
inputData[0][e.
|
|
2215
|
+
inputData[0][e.code] = 4;
|
|
2200
2216
|
if (inputWASDEmulateDirection)
|
|
2201
|
-
inputData[0][remapKey(e.
|
|
2217
|
+
inputData[0][remapKey(e.code)] = 4;
|
|
2202
2218
|
}
|
|
2203
2219
|
|
|
2204
2220
|
// handle remapping wasd keys to directions
|
|
2205
2221
|
function remapKey(c)
|
|
2206
|
-
{
|
|
2222
|
+
{
|
|
2207
2223
|
return inputWASDEmulateDirection ?
|
|
2208
|
-
c
|
|
2224
|
+
c == 'KeyW' ? 'ArrowUp' :
|
|
2225
|
+
c == 'KeyS' ? 'ArrowDown' :
|
|
2226
|
+
c == 'KeyA' ? 'ArrowLeft' :
|
|
2227
|
+
c == 'KeyD' ? 'ArrowRight' : c : c;
|
|
2209
2228
|
}
|
|
2210
2229
|
}
|
|
2211
2230
|
|
|
2212
2231
|
///////////////////////////////////////////////////////////////////////////////
|
|
2213
2232
|
// Mouse event handlers
|
|
2214
2233
|
|
|
2215
|
-
onmousedown = (e)=> {
|
|
2234
|
+
onmousedown = (e)=> {isUsingGamepad = false; inputData[0][e.button] = 3; window.onmousemove(e); e.button && e.preventDefault();}
|
|
2216
2235
|
onmouseup = (e)=> inputData[0][e.button] = inputData[0][e.button] & 2 | 4;
|
|
2217
2236
|
onmousemove = (e)=> mousePosScreen = mouseToScreen(e);
|
|
2218
|
-
onwheel
|
|
2237
|
+
onwheel = (e)=> e.ctrlKey || (mouseWheel = sign(e.deltaY));
|
|
2219
2238
|
oncontextmenu = (e)=> false; // prevent right click menu
|
|
2220
2239
|
|
|
2221
2240
|
// convert a mouse or touch event position to screen space
|
|
@@ -2253,7 +2272,7 @@ function gamepadsUpdate()
|
|
|
2253
2272
|
for (let i=10; i--;)
|
|
2254
2273
|
{
|
|
2255
2274
|
const j = i == 3 ? 2 : i == 2 ? 3 : i; // fix button locations
|
|
2256
|
-
data[j] = touchGamepadButtons[i] ?
|
|
2275
|
+
data[j] = touchGamepadButtons[i] ? gamepadIsDown(j,0) ? 1 : 3 : gamepadIsDown(j,0) ? 4 : 0;
|
|
2257
2276
|
}
|
|
2258
2277
|
}
|
|
2259
2278
|
}
|
|
@@ -2285,18 +2304,23 @@ function gamepadsUpdate()
|
|
|
2285
2304
|
for (let j = gamepad.buttons.length; j--;)
|
|
2286
2305
|
{
|
|
2287
2306
|
const button = gamepad.buttons[j];
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2307
|
+
const wasDown = gamepadIsDown(j,i);
|
|
2308
|
+
data[j] = button.pressed ? wasDown ? 1 : 3 : wasDown ? 4 : 0;
|
|
2309
|
+
isUsingGamepad ||= !i && button.pressed;
|
|
2291
2310
|
}
|
|
2292
2311
|
|
|
2293
2312
|
if (gamepadDirectionEmulateStick)
|
|
2294
2313
|
{
|
|
2295
2314
|
// copy dpad to left analog stick when pressed
|
|
2296
|
-
const dpad = vec2(
|
|
2315
|
+
const dpad = vec2(
|
|
2316
|
+
(gamepadIsDown(15,i)&&1) - (gamepadIsDown(14,i)&&1),
|
|
2317
|
+
(gamepadIsDown(12,i)&&1) - (gamepadIsDown(13,i)&&1));
|
|
2297
2318
|
if (dpad.lengthSquared())
|
|
2298
2319
|
sticks[0] = dpad.clampLength();
|
|
2299
2320
|
}
|
|
2321
|
+
|
|
2322
|
+
// disable touch gamepad if using real gamepad
|
|
2323
|
+
touchGamepadEnable && isUsingGamepad && touchGamepadTimer.unset();
|
|
2300
2324
|
}
|
|
2301
2325
|
}
|
|
2302
2326
|
}
|
|
@@ -2304,9 +2328,9 @@ function gamepadsUpdate()
|
|
|
2304
2328
|
///////////////////////////////////////////////////////////////////////////////
|
|
2305
2329
|
|
|
2306
2330
|
/** Pulse the vibration hardware if it exists
|
|
2307
|
-
* @param {Number} [pattern
|
|
2331
|
+
* @param {Number} [pattern] - a single value in miliseconds or vibration interval array
|
|
2308
2332
|
* @memberof Input */
|
|
2309
|
-
function vibrate(pattern)
|
|
2333
|
+
function vibrate(pattern=100)
|
|
2310
2334
|
{ vibrateEnable && navigator && navigator.vibrate && navigator.vibrate(pattern); }
|
|
2311
2335
|
|
|
2312
2336
|
/** Cancel any ongoing vibration
|
|
@@ -2324,29 +2348,28 @@ const isTouchDevice = window.ontouchstart !== undefined;
|
|
|
2324
2348
|
if (isTouchDevice)
|
|
2325
2349
|
{
|
|
2326
2350
|
// override mouse events
|
|
2327
|
-
let wasTouching
|
|
2351
|
+
let wasTouching;
|
|
2328
2352
|
onmousedown = onmouseup = ()=> 0;
|
|
2329
2353
|
|
|
2330
2354
|
// handle all touch events the same way
|
|
2331
2355
|
ontouchstart = ontouchmove = ontouchend = (e)=>
|
|
2332
2356
|
{
|
|
2333
|
-
e.button = 0; // all touches are left click
|
|
2334
|
-
|
|
2335
2357
|
// fix stalled audio on mobile
|
|
2336
2358
|
if (soundEnable)
|
|
2337
2359
|
audioContext ? audioContext.resume() : zzfx(0);
|
|
2338
2360
|
|
|
2339
2361
|
// check if touching and pass to mouse events
|
|
2340
2362
|
const touching = e.touches.length;
|
|
2363
|
+
const button = 0; // all touches are left mouse button
|
|
2341
2364
|
if (touching)
|
|
2342
2365
|
{
|
|
2343
2366
|
// set event pos and pass it along
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
wasTouching ?
|
|
2367
|
+
const p = vec2(e.touches[0].clientX, e.touches[0].clientY);
|
|
2368
|
+
mousePosScreen = mouseToScreen(p);
|
|
2369
|
+
wasTouching ? isUsingGamepad = false : inputData[0][button] = 3;
|
|
2347
2370
|
}
|
|
2348
2371
|
else if (wasTouching)
|
|
2349
|
-
|
|
2372
|
+
inputData[0][button] = inputData[0][button] & 2 | 4;
|
|
2350
2373
|
|
|
2351
2374
|
// set was touching
|
|
2352
2375
|
wasTouching = touching;
|
|
@@ -2427,8 +2450,8 @@ function createTouchGamepad()
|
|
|
2427
2450
|
}
|
|
2428
2451
|
|
|
2429
2452
|
// call default touch handler and set to using gamepad
|
|
2430
|
-
touchHandler(e);
|
|
2431
|
-
isUsingGamepad =
|
|
2453
|
+
touchHandler.bind(window)(e);
|
|
2454
|
+
isUsingGamepad = true;
|
|
2432
2455
|
|
|
2433
2456
|
// must return true so the document will get focus
|
|
2434
2457
|
return true;
|
|
@@ -2442,7 +2465,7 @@ function touchGamepadRender()
|
|
|
2442
2465
|
return;
|
|
2443
2466
|
|
|
2444
2467
|
// fade off when not touching or paused
|
|
2445
|
-
const alpha = percent(touchGamepadTimer, 4, 3);
|
|
2468
|
+
const alpha = percent(touchGamepadTimer.get(), 4, 3);
|
|
2446
2469
|
if (!alpha || paused)
|
|
2447
2470
|
return;
|
|
2448
2471
|
|
|
@@ -2546,13 +2569,13 @@ class Sound
|
|
|
2546
2569
|
|
|
2547
2570
|
/** Play the sound
|
|
2548
2571
|
* @param {Vector2} [pos] - World space position to play the sound, sound is not attenuated if null
|
|
2549
|
-
* @param {Number} [volume
|
|
2550
|
-
* @param {Number} [pitch
|
|
2551
|
-
* @param {Number} [randomnessScale
|
|
2552
|
-
* @param {Boolean} [loop
|
|
2572
|
+
* @param {Number} [volume] - How much to scale volume by (in addition to range fade)
|
|
2573
|
+
* @param {Number} [pitch] - How much to scale pitch by (also adjusted by this.randomness)
|
|
2574
|
+
* @param {Number} [randomnessScale] - How much to scale randomness
|
|
2575
|
+
* @param {Boolean} [loop] - Should the sound loop
|
|
2553
2576
|
* @return {AudioBufferSourceNode} - The audio source node
|
|
2554
2577
|
*/
|
|
2555
|
-
play(pos, volume=1, pitch=1, randomnessScale=1, loop=
|
|
2578
|
+
play(pos, volume=1, pitch=1, randomnessScale=1, loop=false)
|
|
2556
2579
|
{
|
|
2557
2580
|
if (!soundEnable || !this.sampleChannels) return;
|
|
2558
2581
|
|
|
@@ -2585,8 +2608,13 @@ class Sound
|
|
|
2585
2608
|
{
|
|
2586
2609
|
if (this.source)
|
|
2587
2610
|
this.source.stop();
|
|
2588
|
-
this.source =
|
|
2611
|
+
this.source = undefined;
|
|
2589
2612
|
}
|
|
2613
|
+
|
|
2614
|
+
/** Get source of most recent instance of this sound that was played
|
|
2615
|
+
* @return {AudioBufferSourceNode}
|
|
2616
|
+
*/
|
|
2617
|
+
getSource() { return this.source; }
|
|
2590
2618
|
|
|
2591
2619
|
/** Play the sound as a note with a semitone offset
|
|
2592
2620
|
* @param {Number} semitoneOffset - How many semitones to offset pitch
|
|
@@ -2602,12 +2630,7 @@ class Sound
|
|
|
2602
2630
|
*/
|
|
2603
2631
|
getDuration()
|
|
2604
2632
|
{ return this.sampleChannels && this.sampleChannels[0].length / this.sampleRate; }
|
|
2605
|
-
|
|
2606
|
-
/** Check if the last instance of this sound is playing
|
|
2607
|
-
* @return {Boolean} - True if the sound is playing
|
|
2608
|
-
*/
|
|
2609
|
-
isPlaying() { return this.source && !this.source.ended; }
|
|
2610
|
-
|
|
2633
|
+
|
|
2611
2634
|
/** Check if sound is loading, for sounds fetched from a url
|
|
2612
2635
|
* @return {Boolean} - True if sound is loading and not ready to play
|
|
2613
2636
|
*/
|
|
@@ -2628,13 +2651,13 @@ class SoundWave extends Sound
|
|
|
2628
2651
|
{
|
|
2629
2652
|
/** Create a sound object and cache the wave file for later use
|
|
2630
2653
|
* @param {String} filename - Filename of audio file to load
|
|
2631
|
-
* @param {Number} [randomness
|
|
2654
|
+
* @param {Number} [randomness] - How much to randomize frequency each time sound plays
|
|
2632
2655
|
* @param {Number} [range=soundDefaultRange] - World space max range of sound, will not play if camera is farther away
|
|
2633
2656
|
* @param {Number} [taper=soundDefaultTaper] - At what percentage of range should it start tapering off
|
|
2634
2657
|
*/
|
|
2635
2658
|
constructor(filename, randomness=0, range, taper)
|
|
2636
2659
|
{
|
|
2637
|
-
super(
|
|
2660
|
+
super(undefined, range, taper);
|
|
2638
2661
|
this.randomness = randomness;
|
|
2639
2662
|
|
|
2640
2663
|
if (!soundEnable) return;
|
|
@@ -2688,11 +2711,11 @@ let soundDecoderContext; // audio context used only to decode audio files
|
|
|
2688
2711
|
class Music extends Sound
|
|
2689
2712
|
{
|
|
2690
2713
|
/** Create a music object and cache the zzfx music samples for later use
|
|
2691
|
-
* @param {Array} zzfxMusic - Array of zzfx music parameters
|
|
2714
|
+
* @param {[Array, Array, Array, Number]} zzfxMusic - Array of zzfx music parameters
|
|
2692
2715
|
*/
|
|
2693
2716
|
constructor(zzfxMusic)
|
|
2694
2717
|
{
|
|
2695
|
-
super();
|
|
2718
|
+
super(undefined);
|
|
2696
2719
|
|
|
2697
2720
|
if (!soundEnable) return;
|
|
2698
2721
|
this.randomness = 0;
|
|
@@ -2705,17 +2728,17 @@ class Music extends Sound
|
|
|
2705
2728
|
* @param {Boolean} [loop=1] - True if the music should loop
|
|
2706
2729
|
* @return {AudioBufferSourceNode} - The audio source node
|
|
2707
2730
|
*/
|
|
2708
|
-
playMusic(volume, loop =
|
|
2709
|
-
{ return super.play(
|
|
2731
|
+
playMusic(volume, loop = false)
|
|
2732
|
+
{ return super.play(undefined, volume, 1, 1, loop); }
|
|
2710
2733
|
}
|
|
2711
2734
|
|
|
2712
2735
|
/** Play an mp3, ogg, or wav audio from a local file or url
|
|
2713
2736
|
* @param {String} url - Location of sound file to play
|
|
2714
|
-
* @param {Number} [volume
|
|
2715
|
-
* @param {Boolean} [loop
|
|
2737
|
+
* @param {Number} [volume] - How much to scale volume by
|
|
2738
|
+
* @param {Boolean} [loop] - True if the music should loop
|
|
2716
2739
|
* @return {HTMLAudioElement} - The audio element for this sound
|
|
2717
2740
|
* @memberof Audio */
|
|
2718
|
-
function playAudioFile(url, volume=1, loop=
|
|
2741
|
+
function playAudioFile(url, volume=1, loop=false)
|
|
2719
2742
|
{
|
|
2720
2743
|
if (!soundEnable) return;
|
|
2721
2744
|
|
|
@@ -2729,9 +2752,9 @@ function playAudioFile(url, volume=1, loop=1)
|
|
|
2729
2752
|
/** Speak text with passed in settings
|
|
2730
2753
|
* @param {String} text - The text to speak
|
|
2731
2754
|
* @param {String} [language] - The language/accent to use (examples: en, it, ru, ja, zh)
|
|
2732
|
-
* @param {Number} [volume
|
|
2733
|
-
* @param {Number} [rate
|
|
2734
|
-
* @param {Number} [pitch
|
|
2755
|
+
* @param {Number} [volume] - How much to scale volume by
|
|
2756
|
+
* @param {Number} [rate] - How quickly to speak
|
|
2757
|
+
* @param {Number} [pitch] - How much to change the pitch by
|
|
2735
2758
|
* @return {SpeechSynthesisUtterance} - The utterance that was spoken
|
|
2736
2759
|
* @memberof Audio */
|
|
2737
2760
|
function speak(text, language='', volume=1, rate=1, pitch=1)
|
|
@@ -2758,7 +2781,7 @@ function speakStop() {speechSynthesis && speechSynthesis.cancel();}
|
|
|
2758
2781
|
|
|
2759
2782
|
/** Get frequency of a note on a musical scale
|
|
2760
2783
|
* @param {Number} semitoneOffset - How many semitones away from the root note
|
|
2761
|
-
* @param {Number} [
|
|
2784
|
+
* @param {Number} [rootFrequency=220] - Frequency at semitone offset 0
|
|
2762
2785
|
* @return {Number} - The frequency of the note
|
|
2763
2786
|
* @memberof Audio */
|
|
2764
2787
|
function getNoteFrequency(semitoneOffset, rootFrequency=220)
|
|
@@ -2772,14 +2795,14 @@ let audioContext;
|
|
|
2772
2795
|
|
|
2773
2796
|
/** Play cached audio samples with given settings
|
|
2774
2797
|
* @param {Array} sampleChannels - Array of arrays of samples to play (for stereo playback)
|
|
2775
|
-
* @param {Number} [volume
|
|
2776
|
-
* @param {Number} [rate
|
|
2777
|
-
* @param {Number} [pan
|
|
2778
|
-
* @param {Boolean} [loop
|
|
2798
|
+
* @param {Number} [volume] - How much to scale volume by
|
|
2799
|
+
* @param {Number} [rate] - The playback rate to use
|
|
2800
|
+
* @param {Number} [pan] - How much to apply stereo panning
|
|
2801
|
+
* @param {Boolean} [loop] - True if the sound should loop when it reaches the end
|
|
2779
2802
|
* @param {Number} [sampleRate=44100] - Sample rate for the sound
|
|
2780
2803
|
* @return {AudioBufferSourceNode} - The audio node of the sound played
|
|
2781
2804
|
* @memberof Audio */
|
|
2782
|
-
function playSamples(sampleChannels, volume=1, rate=1, pan=0, loop=
|
|
2805
|
+
function playSamples(sampleChannels, volume=1, rate=1, pan=0, loop=false, sampleRate=zzfxR)
|
|
2783
2806
|
{
|
|
2784
2807
|
if (!soundEnable) return;
|
|
2785
2808
|
|
|
@@ -2819,7 +2842,7 @@ function playSamples(sampleChannels, volume=1, rate=1, pan=0, loop=0, sampleRate
|
|
|
2819
2842
|
}
|
|
2820
2843
|
|
|
2821
2844
|
///////////////////////////////////////////////////////////////////////////////
|
|
2822
|
-
// ZzFXMicro - Zuper Zmall Zound Zynth - v1.3.
|
|
2845
|
+
// ZzFXMicro - Zuper Zmall Zound Zynth - v1.3.1 by Frank Force
|
|
2823
2846
|
|
|
2824
2847
|
/** Generate and play a ZzFX sound
|
|
2825
2848
|
*
|
|
@@ -2835,27 +2858,27 @@ function zzfx(...zzfxSound) { return playSamples([zzfxG(...zzfxSound)]); }
|
|
|
2835
2858
|
const zzfxR = 44100;
|
|
2836
2859
|
|
|
2837
2860
|
/** Generate samples for a ZzFX sound
|
|
2838
|
-
* @param {Number} [volume
|
|
2839
|
-
* @param {Number} [randomness
|
|
2840
|
-
* @param {Number} [frequency
|
|
2841
|
-
* @param {Number} [attack
|
|
2842
|
-
* @param {Number} [sustain
|
|
2843
|
-
* @param {Number} [release
|
|
2844
|
-
* @param {Number} [shape
|
|
2845
|
-
* @param {Number} [shapeCurve
|
|
2846
|
-
* @param {Number} [slide
|
|
2847
|
-
* @param {Number} [deltaSlide
|
|
2848
|
-
* @param {Number} [pitchJump
|
|
2849
|
-
* @param {Number} [pitchJumpTime
|
|
2850
|
-
* @param {Number} [repeatTime
|
|
2851
|
-
* @param {Number} [noise
|
|
2852
|
-
* @param {Number} [modulation
|
|
2853
|
-
* @param {Number} [bitCrush
|
|
2854
|
-
* @param {Number} [delay
|
|
2855
|
-
* @param {Number} [sustainVolume
|
|
2856
|
-
* @param {Number} [decay
|
|
2857
|
-
* @param {Number} [tremolo
|
|
2858
|
-
* @param {Number} [filter
|
|
2861
|
+
* @param {Number} [volume] - Volume scale (percent)
|
|
2862
|
+
* @param {Number} [randomness] - How much to randomize frequency (percent Hz)
|
|
2863
|
+
* @param {Number} [frequency] - Frequency of sound (Hz)
|
|
2864
|
+
* @param {Number} [attack] - Attack time, how fast sound starts (seconds)
|
|
2865
|
+
* @param {Number} [sustain] - Sustain time, how long sound holds (seconds)
|
|
2866
|
+
* @param {Number} [release] - Release time, how fast sound fades out (seconds)
|
|
2867
|
+
* @param {Number} [shape] - Shape of the sound wave
|
|
2868
|
+
* @param {Number} [shapeCurve] - Squarenes of wave (0=square, 1=normal, 2=pointy)
|
|
2869
|
+
* @param {Number} [slide] - How much to slide frequency (kHz/s)
|
|
2870
|
+
* @param {Number} [deltaSlide] - How much to change slide (kHz/s/s)
|
|
2871
|
+
* @param {Number} [pitchJump] - Frequency of pitch jump (Hz)
|
|
2872
|
+
* @param {Number} [pitchJumpTime] - Time of pitch jump (seconds)
|
|
2873
|
+
* @param {Number} [repeatTime] - Resets some parameters periodically (seconds)
|
|
2874
|
+
* @param {Number} [noise] - How much random noise to add (percent)
|
|
2875
|
+
* @param {Number} [modulation] - Frequency of modulation wave, negative flips phase (Hz)
|
|
2876
|
+
* @param {Number} [bitCrush] - Resamples at a lower frequency in (samples*100)
|
|
2877
|
+
* @param {Number} [delay] - Overlap sound with itself for reverb and flanger effects (seconds)
|
|
2878
|
+
* @param {Number} [sustainVolume] - Volume level for sustain (percent)
|
|
2879
|
+
* @param {Number} [decay] - Decay time, how long to reach sustain after attack (seconds)
|
|
2880
|
+
* @param {Number} [tremolo] - Trembling effect, rate controlled by repeat time (precent)
|
|
2881
|
+
* @param {Number} [filter] - Filter cutoff frequency, positive for HPF, negative for LPF (Hz)
|
|
2859
2882
|
* @return {Array} - Array of audio samples
|
|
2860
2883
|
* @memberof Audio
|
|
2861
2884
|
*/
|
|
@@ -2903,7 +2926,7 @@ function zzfxG
|
|
|
2903
2926
|
if (!(++c%(bitCrush*100|0))) // bit crush
|
|
2904
2927
|
{
|
|
2905
2928
|
s = shape? shape>1? shape>2? shape>3? // wave shape
|
|
2906
|
-
Math.sin(t
|
|
2929
|
+
Math.sin(t**3) : // 4 noise
|
|
2907
2930
|
clamp(Math.tan(t),1,-1): // 3 tan
|
|
2908
2931
|
1-(2*t/PI2%2+2)%2: // 2 saw
|
|
2909
2932
|
1-4*abs(Math.round(t/PI2)-t/PI2): // 1 triangle
|
|
@@ -2960,7 +2983,7 @@ function zzfxG
|
|
|
2960
2983
|
* @param {Array} instruments - Array of ZzFX sound paramaters
|
|
2961
2984
|
* @param {Array} patterns - Array of pattern data
|
|
2962
2985
|
* @param {Array} sequence - Array of pattern indexes
|
|
2963
|
-
* @param {Number} [BPM
|
|
2986
|
+
* @param {Number} [BPM] - Playback speed of the song in BPM
|
|
2964
2987
|
* @return {Array} - Left and right channel sample data
|
|
2965
2988
|
* @memberof Audio */
|
|
2966
2989
|
function zzfxM(instruments, patterns, sequence, BPM = 125)
|
|
@@ -2999,10 +3022,10 @@ function zzfxM(instruments, patterns, sequence, BPM = 125)
|
|
|
2999
3022
|
patternChannel = patterns[patternIndex][channelIndex] || [0, 0, 0];
|
|
3000
3023
|
|
|
3001
3024
|
// check if there are more channels
|
|
3002
|
-
hasMore
|
|
3025
|
+
hasMore |= patterns[patternIndex][channelIndex]&&1;
|
|
3003
3026
|
|
|
3004
3027
|
// get next offset, use the length of first channel
|
|
3005
|
-
nextSampleOffset = outSampleOffset + (patterns[patternIndex][0].length - 2 -
|
|
3028
|
+
nextSampleOffset = outSampleOffset + (patterns[patternIndex][0].length - 2 - (notFirstBeat?0:1)) * beatLength;
|
|
3006
3029
|
// for each beat in pattern, plus one extra if end of sequence
|
|
3007
3030
|
isSequenceEnd = sequenceIndex == sequence.length - 1;
|
|
3008
3031
|
for (i = 2, k = outSampleOffset; i < patternChannel.length + isSequenceEnd; notFirstBeat = ++i) {
|
|
@@ -3018,7 +3041,7 @@ function zzfxM(instruments, patterns, sequence, BPM = 125)
|
|
|
3018
3041
|
for (j = 0; j < beatLength && notFirstBeat;
|
|
3019
3042
|
|
|
3020
3043
|
// fade off attenuation at end of beat if stopping note, prevents clicking
|
|
3021
|
-
j++ > beatLength - 99 && stop
|
|
3044
|
+
j++ > beatLength - 99 && stop && attenuation < 1? attenuation += 1 / 99 : 0
|
|
3022
3045
|
) {
|
|
3023
3046
|
// copy sample to stereo buffers with panning
|
|
3024
3047
|
sample = (1 - attenuation) * sampleBuffer[sampleOffset++] / 2 || 0;
|
|
@@ -3094,7 +3117,7 @@ function initTileCollision(size)
|
|
|
3094
3117
|
|
|
3095
3118
|
/** Set tile collision data
|
|
3096
3119
|
* @param {Vector2} pos
|
|
3097
|
-
* @param {Number} [data
|
|
3120
|
+
* @param {Number} [data]
|
|
3098
3121
|
* @memberof TileCollision */
|
|
3099
3122
|
function setTileCollisionData(pos, data=0)
|
|
3100
3123
|
{
|
|
@@ -3127,7 +3150,7 @@ function tileCollisionTest(pos, size=vec2(), object)
|
|
|
3127
3150
|
{
|
|
3128
3151
|
const tileData = tileCollision[y*tileCollisionSize.x+x];
|
|
3129
3152
|
if (tileData && (!object || object.collideWithTile(tileData, vec2(x, y))))
|
|
3130
|
-
return
|
|
3153
|
+
return true;
|
|
3131
3154
|
}
|
|
3132
3155
|
}
|
|
3133
3156
|
|
|
@@ -3193,11 +3216,11 @@ function tileCollisionRaycast(posStart, posEnd, object)
|
|
|
3193
3216
|
class TileLayerData
|
|
3194
3217
|
{
|
|
3195
3218
|
/** Create a tile layer data object, one for each tile in a TileLayer
|
|
3196
|
-
* @param {Number} [tile]
|
|
3197
|
-
* @param {Number} [direction
|
|
3198
|
-
* @param {Boolean} [mirror
|
|
3199
|
-
* @param {Color} [color
|
|
3200
|
-
constructor(tile, direction=0, mirror=
|
|
3219
|
+
* @param {Number} [tile] - The tile to use, untextured if undefined
|
|
3220
|
+
* @param {Number} [direction] - Integer direction of tile, in 90 degree increments
|
|
3221
|
+
* @param {Boolean} [mirror] - If the tile should be mirrored along the x axis
|
|
3222
|
+
* @param {Color} [color] - Color of the tile */
|
|
3223
|
+
constructor(tile, direction=0, mirror=false, color=new Color())
|
|
3201
3224
|
{
|
|
3202
3225
|
/** @property {Number} - The tile to use, untextured if undefined */
|
|
3203
3226
|
this.tile = tile;
|
|
@@ -3210,7 +3233,7 @@ class TileLayerData
|
|
|
3210
3233
|
}
|
|
3211
3234
|
|
|
3212
3235
|
/** Set this tile to clear, it will not be rendered */
|
|
3213
|
-
clear() { this.tile = this.direction = this.mirror =
|
|
3236
|
+
clear() { this.tile = this.direction = 0; this.mirror = false; this.color = new Color; }
|
|
3214
3237
|
}
|
|
3215
3238
|
|
|
3216
3239
|
/**
|
|
@@ -3227,25 +3250,25 @@ class TileLayerData
|
|
|
3227
3250
|
*/
|
|
3228
3251
|
class TileLayer extends EngineObject
|
|
3229
3252
|
{
|
|
3230
|
-
/** Create a tile layer object
|
|
3231
|
-
* @param {Vector2}
|
|
3232
|
-
* @param {Vector2}
|
|
3233
|
-
* @param {TileInfo} [tileInfo]
|
|
3234
|
-
* @param {Vector2}
|
|
3235
|
-
* @param {Number}
|
|
3253
|
+
/** Create a tile layer object
|
|
3254
|
+
* @param {Vector2} [position=Vector2()] - World space position
|
|
3255
|
+
* @param {Vector2} [size=tileCollisionSize] - World space size
|
|
3256
|
+
* @param {TileInfo} [tileInfo] - Tile info for layer
|
|
3257
|
+
* @param {Vector2} [scale=Vector2(1,1)] - How much to scale this layer when rendered
|
|
3258
|
+
* @param {Number} [renderOrder] - Objects sorted by renderOrder before being rendered
|
|
3236
3259
|
*/
|
|
3237
|
-
constructor(
|
|
3260
|
+
constructor(position, size=tileCollisionSize, tileInfo=tile(), scale=vec2(1), renderOrder=0)
|
|
3238
3261
|
{
|
|
3239
|
-
super(
|
|
3262
|
+
super(position, size, tileInfo, 0, undefined, renderOrder);
|
|
3240
3263
|
|
|
3241
|
-
/** @property {HTMLCanvasElement}
|
|
3264
|
+
/** @property {HTMLCanvasElement} - The canvas used by this tile layer */
|
|
3242
3265
|
this.canvas = document.createElement('canvas');
|
|
3243
3266
|
/** @property {CanvasRenderingContext2D} - The 2D canvas context used by this tile layer */
|
|
3244
3267
|
this.context = this.canvas.getContext('2d');
|
|
3245
|
-
/** @property {Vector2}
|
|
3268
|
+
/** @property {Vector2} - How much to scale this layer when rendered */
|
|
3246
3269
|
this.scale = scale;
|
|
3247
|
-
/** @property {Boolean}
|
|
3248
|
-
this.isOverlay;
|
|
3270
|
+
/** @property {Boolean} - If true this layer will render to overlay canvas and appear above all objects */
|
|
3271
|
+
this.isOverlay = false;
|
|
3249
3272
|
|
|
3250
3273
|
// init tile data
|
|
3251
3274
|
this.data = [];
|
|
@@ -3254,10 +3277,10 @@ constructor(pos, size=tileCollisionSize, tileInfo=tile(), scale=vec2(1), renderO
|
|
|
3254
3277
|
}
|
|
3255
3278
|
|
|
3256
3279
|
/** Set data at a given position in the array
|
|
3257
|
-
* @param {Vector2}
|
|
3258
|
-
* @param {TileLayerData} data
|
|
3259
|
-
* @param {Boolean} [redraw
|
|
3260
|
-
setData(layerPos, data, redraw)
|
|
3280
|
+
* @param {Vector2} layerPos - Local position in array
|
|
3281
|
+
* @param {TileLayerData} data - Data to set
|
|
3282
|
+
* @param {Boolean} [redraw] - Force the tile to redraw if true */
|
|
3283
|
+
setData(layerPos, data, redraw=false)
|
|
3261
3284
|
{
|
|
3262
3285
|
if (layerPos.arrayCheck(this.size))
|
|
3263
3286
|
{
|
|
@@ -3278,7 +3301,7 @@ constructor(pos, size=tileCollisionSize, tileInfo=tile(), scale=vec2(1), renderO
|
|
|
3278
3301
|
// Render the tile layer, called automatically by the engine
|
|
3279
3302
|
render()
|
|
3280
3303
|
{
|
|
3281
|
-
ASSERT(mainContext != this.context
|
|
3304
|
+
ASSERT(mainContext != this.context, 'must call redrawEnd() after drawing tiles');
|
|
3282
3305
|
|
|
3283
3306
|
// flush and copy gl canvas because tile canvas does not use webgl
|
|
3284
3307
|
glEnable && !glOverlay && !this.isOverlay && glCopyToContext(mainContext);
|
|
@@ -3297,16 +3320,17 @@ constructor(pos, size=tileCollisionSize, tileInfo=tile(), scale=vec2(1), renderO
|
|
|
3297
3320
|
*/
|
|
3298
3321
|
redraw()
|
|
3299
3322
|
{
|
|
3300
|
-
this.redrawStart(
|
|
3323
|
+
this.redrawStart(true);
|
|
3301
3324
|
this.drawAllTileData();
|
|
3302
3325
|
this.redrawEnd();
|
|
3303
3326
|
}
|
|
3304
3327
|
|
|
3305
3328
|
/** Call to start the redraw process
|
|
3306
|
-
* @param {Boolean} [clear
|
|
3307
|
-
redrawStart(clear
|
|
3329
|
+
* @param {Boolean} [clear] - Should it clear the canvas before drawing */
|
|
3330
|
+
redrawStart(clear=false)
|
|
3308
3331
|
{
|
|
3309
3332
|
// save current render settings
|
|
3333
|
+
/** @type {[HTMLCanvasElement, CanvasRenderingContext2D, Vector2, Vector2, number]} */
|
|
3310
3334
|
this.savedRenderSettings = [mainCanvas, mainContext, mainCanvasSize, cameraPos, cameraScale];
|
|
3311
3335
|
|
|
3312
3336
|
// hack: use normal rendering system to render the tiles
|
|
@@ -3329,8 +3353,8 @@ constructor(pos, size=tileCollisionSize, tileInfo=tile(), scale=vec2(1), renderO
|
|
|
3329
3353
|
/** Call to end the redraw process */
|
|
3330
3354
|
redrawEnd()
|
|
3331
3355
|
{
|
|
3332
|
-
ASSERT(mainContext == this.context
|
|
3333
|
-
glEnable && glCopyToContext(mainContext,
|
|
3356
|
+
ASSERT(mainContext == this.context, 'must call redrawStart() before drawing tiles');
|
|
3357
|
+
glEnable && glCopyToContext(mainContext, true);
|
|
3334
3358
|
//debugSaveCanvas(this.canvas);
|
|
3335
3359
|
|
|
3336
3360
|
// set stuff back to normal
|
|
@@ -3343,13 +3367,13 @@ constructor(pos, size=tileCollisionSize, tileInfo=tile(), scale=vec2(1), renderO
|
|
|
3343
3367
|
{
|
|
3344
3368
|
// first clear out where the tile was
|
|
3345
3369
|
const pos = layerPos.floor().add(this.pos).add(vec2(.5));
|
|
3346
|
-
this.drawCanvas2D(pos, vec2(1), 0,
|
|
3370
|
+
this.drawCanvas2D(pos, vec2(1), 0, false, (context)=>context.clearRect(-.5, -.5, 1, 1));
|
|
3347
3371
|
|
|
3348
3372
|
// draw the tile if not undefined
|
|
3349
3373
|
const d = this.getData(layerPos);
|
|
3350
3374
|
if (d.tile != undefined)
|
|
3351
3375
|
{
|
|
3352
|
-
ASSERT(mainContext == this.context
|
|
3376
|
+
ASSERT(mainContext == this.context, 'must call redrawStart() before drawing tiles');
|
|
3353
3377
|
const tileInfo = tile(d.tile, this.tileInfo.size, this.tileInfo.textureIndex);
|
|
3354
3378
|
drawTile(pos, vec2(1), tileInfo, d.color, d.direction*PI/2, d.mirror);
|
|
3355
3379
|
}
|
|
@@ -3417,7 +3441,7 @@ constructor(pos, size=tileCollisionSize, tileInfo=tile(), scale=vec2(1), renderO
|
|
|
3417
3441
|
* @param {Color} [color=Color()]
|
|
3418
3442
|
* @param {Number} [angle=0] */
|
|
3419
3443
|
drawRect(pos, size, color, angle)
|
|
3420
|
-
{ this.drawTile(pos, size,
|
|
3444
|
+
{ this.drawTile(pos, size, undefined, color, angle); }
|
|
3421
3445
|
}
|
|
3422
3446
|
/**
|
|
3423
3447
|
* LittleJS Particle System
|
|
@@ -3446,36 +3470,36 @@ class ParticleEmitter extends EngineObject
|
|
|
3446
3470
|
{
|
|
3447
3471
|
/** Create a particle system with the given settings
|
|
3448
3472
|
* @param {Vector2} position - World space position of the emitter
|
|
3449
|
-
* @param {Number}
|
|
3450
|
-
* @param {Number|Vector2} [emitSize
|
|
3451
|
-
* @param {Number}
|
|
3452
|
-
* @param {Number}
|
|
3453
|
-
* @param {Number}
|
|
3473
|
+
* @param {Number} [angle] - Angle to emit the particles
|
|
3474
|
+
* @param {Number|Vector2} [emitSize] - World space size of the emitter (float for circle diameter, vec2 for rect)
|
|
3475
|
+
* @param {Number} [emitTime] - How long to stay alive (0 is forever)
|
|
3476
|
+
* @param {Number} [emitRate] - How many particles per second to spawn, does not emit if 0
|
|
3477
|
+
* @param {Number} [emitConeAngle=PI] - Local angle to apply velocity to particles from emitter
|
|
3454
3478
|
* @param {TileInfo} [tileInfo] - Tile info to render particles (undefined is untextured)
|
|
3455
|
-
* @param {Color}
|
|
3456
|
-
* @param {Color}
|
|
3457
|
-
* @param {Color}
|
|
3458
|
-
* @param {Color}
|
|
3459
|
-
* @param {Number}
|
|
3460
|
-
* @param {Number}
|
|
3461
|
-
* @param {Number}
|
|
3462
|
-
* @param {Number}
|
|
3463
|
-
* @param {Number}
|
|
3464
|
-
* @param {Number}
|
|
3465
|
-
* @param {Number}
|
|
3466
|
-
* @param {Number}
|
|
3467
|
-
* @param {Number}
|
|
3468
|
-
* @param {Number}
|
|
3469
|
-
* @param {Number}
|
|
3470
|
-
* @param {Boolean} [collideTiles
|
|
3471
|
-
* @param {Boolean} [additive
|
|
3472
|
-
* @param {Boolean} [randomColorLinear
|
|
3473
|
-
* @param {Number}
|
|
3474
|
-
* @param {Boolean} [localSpace
|
|
3479
|
+
* @param {Color} [colorStartA=Color()] - Color at start of life 1, randomized between start colors
|
|
3480
|
+
* @param {Color} [colorStartB=Color()] - Color at start of life 2, randomized between start colors
|
|
3481
|
+
* @param {Color} [colorEndA=Color(1,1,1,0)] - Color at end of life 1, randomized between end colors
|
|
3482
|
+
* @param {Color} [colorEndB=Color(1,1,1,0)] - Color at end of life 2, randomized between end colors
|
|
3483
|
+
* @param {Number} [particleTime] - How long particles live
|
|
3484
|
+
* @param {Number} [sizeStart] - How big are particles at start
|
|
3485
|
+
* @param {Number} [sizeEnd] - How big are particles at end
|
|
3486
|
+
* @param {Number} [speed] - How fast are particles when spawned
|
|
3487
|
+
* @param {Number} [angleSpeed] - How fast are particles rotating
|
|
3488
|
+
* @param {Number} [damping] - How much to dampen particle speed
|
|
3489
|
+
* @param {Number} [angleDamping] - How much to dampen particle angular speed
|
|
3490
|
+
* @param {Number} [gravityScale] - How much gravity effect particles
|
|
3491
|
+
* @param {Number} [particleConeAngle] - Cone for start particle angle
|
|
3492
|
+
* @param {Number} [fadeRate] - How quick to fade particles at start/end in percent of life
|
|
3493
|
+
* @param {Number} [randomness] - Apply extra randomness percent
|
|
3494
|
+
* @param {Boolean} [collideTiles] - Do particles collide against tiles
|
|
3495
|
+
* @param {Boolean} [additive] - Should particles use addtive blend
|
|
3496
|
+
* @param {Boolean} [randomColorLinear] - Should color be randomized linearly or across each component
|
|
3497
|
+
* @param {Number} [renderOrder] - Render order for particles (additive is above other stuff by default)
|
|
3498
|
+
* @param {Boolean} [localSpace] - Should it be in local space of emitter (world space is default)
|
|
3475
3499
|
*/
|
|
3476
3500
|
constructor
|
|
3477
3501
|
(
|
|
3478
|
-
|
|
3502
|
+
position,
|
|
3479
3503
|
angle,
|
|
3480
3504
|
emitSize = 0,
|
|
3481
3505
|
emitTime = 0,
|
|
@@ -3497,14 +3521,14 @@ class ParticleEmitter extends EngineObject
|
|
|
3497
3521
|
particleConeAngle = PI,
|
|
3498
3522
|
fadeRate = .1,
|
|
3499
3523
|
randomness = .2,
|
|
3500
|
-
collideTiles,
|
|
3501
|
-
additive,
|
|
3502
|
-
randomColorLinear =
|
|
3524
|
+
collideTiles = false,
|
|
3525
|
+
additive = false,
|
|
3526
|
+
randomColorLinear = true,
|
|
3503
3527
|
renderOrder = additive ? 1e9 : 0,
|
|
3504
|
-
localSpace
|
|
3528
|
+
localSpace = false
|
|
3505
3529
|
)
|
|
3506
3530
|
{
|
|
3507
|
-
super(
|
|
3531
|
+
super(position, vec2(), tileInfo, angle, undefined, renderOrder);
|
|
3508
3532
|
|
|
3509
3533
|
// emitter settings
|
|
3510
3534
|
/** @property {Number|Vector2} - World space size of the emitter (float for circle diameter, vec2 for rect) */
|
|
@@ -3553,14 +3577,17 @@ class ParticleEmitter extends EngineObject
|
|
|
3553
3577
|
this.randomness = randomness;
|
|
3554
3578
|
/** @property {Boolean} - Do particles collide against tiles */
|
|
3555
3579
|
this.collideTiles = collideTiles;
|
|
3556
|
-
/** @property {
|
|
3580
|
+
/** @property {Boolean} - Should particles use addtive blend */
|
|
3557
3581
|
this.additive = additive;
|
|
3558
3582
|
/** @property {Boolean} - Should it be in local space of emitter */
|
|
3559
|
-
this.localSpace
|
|
3560
|
-
/** @property {Number} - If
|
|
3583
|
+
this.localSpace = localSpace;
|
|
3584
|
+
/** @property {Number} - If non zero the partile is drawn as a trail, stretched in the drection of velocity */
|
|
3561
3585
|
this.trailScale = 0;
|
|
3562
|
-
|
|
3563
|
-
|
|
3586
|
+
/** @property {Function} - Callback when particle is destroyed */
|
|
3587
|
+
this.particleDestroyCallback = undefined;
|
|
3588
|
+
/** @property {Function} - Callback when particle is created */
|
|
3589
|
+
this.particleCreateCallback = undefined;
|
|
3590
|
+
/** @property {Number} - Track particle emit time */
|
|
3564
3591
|
this.emitTimeBuffer = 0;
|
|
3565
3592
|
}
|
|
3566
3593
|
|
|
@@ -3592,18 +3619,16 @@ class ParticleEmitter extends EngineObject
|
|
|
3592
3619
|
emitParticle()
|
|
3593
3620
|
{
|
|
3594
3621
|
// spawn a particle
|
|
3595
|
-
let pos = this.emitSize
|
|
3596
|
-
|
|
3597
|
-
|
|
3598
|
-
|
|
3622
|
+
let pos = typeof this.emitSize === 'number' ? // check if number was used
|
|
3623
|
+
randInCircle(this.emitSize/2) // circle emitter
|
|
3624
|
+
: vec2(rand(-.5,.5), rand(-.5,.5)) // box emitter
|
|
3625
|
+
.multiply(this.emitSize).rotate(this.angle)
|
|
3599
3626
|
let angle = rand(this.particleConeAngle, -this.particleConeAngle);
|
|
3600
3627
|
if (!this.localSpace)
|
|
3601
3628
|
{
|
|
3602
3629
|
pos = this.pos.add(pos);
|
|
3603
3630
|
angle += this.angle;
|
|
3604
3631
|
}
|
|
3605
|
-
|
|
3606
|
-
const particle = new Particle(pos, this.tileInfo, this.tileSize, angle);
|
|
3607
3632
|
|
|
3608
3633
|
// randomness scales each paremeter by a percentage
|
|
3609
3634
|
const randomness = this.randomness;
|
|
@@ -3619,30 +3644,21 @@ class ParticleEmitter extends EngineObject
|
|
|
3619
3644
|
const colorStart = randColor(this.colorStartA, this.colorStartB, this.randomColorLinear);
|
|
3620
3645
|
const colorEnd = randColor(this.colorEndA, this.colorEndB, this.randomColorLinear);
|
|
3621
3646
|
const velocityAngle = this.localSpace ? coneAngle : this.angle + coneAngle;
|
|
3622
|
-
|
|
3623
|
-
// build particle
|
|
3624
|
-
particle.colorStart
|
|
3625
|
-
particle.
|
|
3626
|
-
particle.
|
|
3627
|
-
particle.
|
|
3628
|
-
particle.
|
|
3629
|
-
particle.
|
|
3630
|
-
particle.
|
|
3631
|
-
particle.
|
|
3632
|
-
particle.
|
|
3633
|
-
particle.
|
|
3634
|
-
particle.
|
|
3635
|
-
|
|
3636
|
-
particle
|
|
3637
|
-
particle.collideTiles = this.collideTiles;
|
|
3638
|
-
particle.additive = this.additive;
|
|
3639
|
-
particle.renderOrder = this.renderOrder;
|
|
3640
|
-
particle.trailScale = this.trailScale;
|
|
3641
|
-
particle.mirror = randInt(2);
|
|
3642
|
-
particle.localSpaceEmitter = this.localSpace && this;
|
|
3643
|
-
|
|
3644
|
-
// setup callbacks for particles
|
|
3645
|
-
particle.destroyCallback = this.particleDestroyCallback;
|
|
3647
|
+
|
|
3648
|
+
// build particle
|
|
3649
|
+
const particle = new Particle(pos, this.tileInfo, angle, colorStart, colorEnd, particleTime, sizeStart, sizeEnd, this.fadeRate, this.additive, this.trailScale, this.localSpace && this, this.particleDestroyCallback);
|
|
3650
|
+
particle.velocity = vec2().setAngle(velocityAngle, speed);
|
|
3651
|
+
particle.fadeRate = this.fadeRate;
|
|
3652
|
+
particle.damping = this.damping;
|
|
3653
|
+
particle.angleDamping = this.angleDamping;
|
|
3654
|
+
particle.elasticity = this.elasticity;
|
|
3655
|
+
particle.friction = this.friction;
|
|
3656
|
+
particle.gravityScale = this.gravityScale;
|
|
3657
|
+
particle.collideTiles = this.collideTiles;
|
|
3658
|
+
particle.renderOrder = this.renderOrder;
|
|
3659
|
+
particle.mirror = !!randInt(2);
|
|
3660
|
+
|
|
3661
|
+
// call particle create callaback
|
|
3646
3662
|
this.particleCreateCallback && this.particleCreateCallback(particle);
|
|
3647
3663
|
|
|
3648
3664
|
// return the newly created particle
|
|
@@ -3661,13 +3677,47 @@ class ParticleEmitter extends EngineObject
|
|
|
3661
3677
|
class Particle extends EngineObject
|
|
3662
3678
|
{
|
|
3663
3679
|
/**
|
|
3664
|
-
* Create a particle with the given
|
|
3665
|
-
* @param {Vector2}
|
|
3666
|
-
* @param {TileInfo} [tileInfo]
|
|
3667
|
-
* @param {Number}
|
|
3680
|
+
* Create a particle with the given shis.colorStart = undefined;ettings
|
|
3681
|
+
* @param {Vector2} position - World space position of the particle
|
|
3682
|
+
* @param {TileInfo} [tileInfo] - Tile info to render particles
|
|
3683
|
+
* @param {Number} [angle] - Angle to rotate the particle
|
|
3684
|
+
* @param {Color} [colorStart] - Color at start of life
|
|
3685
|
+
* @param {Color} [colorEnd] - Color at end of life
|
|
3686
|
+
* @param {Number} [lifeTime] - How long to live for
|
|
3687
|
+
* @param {Number} [sizeStart] - Angle to rotate the particle
|
|
3688
|
+
* @param {Number} [sizeEnd] - Angle to rotate the particle
|
|
3689
|
+
* @param {Number} [fadeRate] - Angle to rotate the particle
|
|
3690
|
+
* @param {Boolean} [additive] - Angle to rotate the particle
|
|
3691
|
+
* @param {Number} [trailScale] - If a trail, how long to make it
|
|
3692
|
+
* @param {ParticleEmitter} [localSpaceEmitter] - Parent emitter if local space
|
|
3693
|
+
* @param {Function} [destroyCallback] - Called when particle dies
|
|
3668
3694
|
*/
|
|
3669
|
-
constructor(
|
|
3670
|
-
|
|
3695
|
+
constructor(position, tileInfo, angle, colorStart, colorEnd, lifeTime, sizeStart, sizeEnd, fadeRate, additive, trailScale, localSpaceEmitter, destroyCallback
|
|
3696
|
+
)
|
|
3697
|
+
{
|
|
3698
|
+
super(position, vec2(), tileInfo, angle);
|
|
3699
|
+
|
|
3700
|
+
/** @property {Color} - Color at start of life */
|
|
3701
|
+
this.colorStart = colorStart;
|
|
3702
|
+
/** @property {Color} - Calculated change in color */
|
|
3703
|
+
this.colorEndDelta = colorEnd.subtract(colorStart);
|
|
3704
|
+
/** @property {Number} - How long to live for */
|
|
3705
|
+
this.lifeTime = lifeTime;
|
|
3706
|
+
/** @property {Number} - Size at start of life */
|
|
3707
|
+
this.sizeStart = sizeStart;
|
|
3708
|
+
/** @property {Number} - Calculated change in size */
|
|
3709
|
+
this.sizeEndDelta = sizeEnd - sizeStart;
|
|
3710
|
+
/** @property {Number} - How quick to fade in/out */
|
|
3711
|
+
this.fadeRate = fadeRate;
|
|
3712
|
+
/** @property {Boolean} - Is it additive */
|
|
3713
|
+
this.additive = additive;
|
|
3714
|
+
/** @property {Number} - If a trail, how long to make it */
|
|
3715
|
+
this.trailScale = trailScale;
|
|
3716
|
+
/** @property {ParticleEmitter} - Parent emitter if local space */
|
|
3717
|
+
this.localSpaceEmitter = localSpaceEmitter;
|
|
3718
|
+
/** @property {Function} - Called when particle dies */
|
|
3719
|
+
this.destroyCallback = destroyCallback;
|
|
3720
|
+
}
|
|
3671
3721
|
|
|
3672
3722
|
/** Render the particle, automatically called each frame, sorted by renderOrder */
|
|
3673
3723
|
render()
|
|
@@ -3685,7 +3735,7 @@ class Particle extends EngineObject
|
|
|
3685
3735
|
(p < fadeRate ? p/fadeRate : p > 1-fadeRate ? (1-p)/fadeRate : 1)); // fade alpha
|
|
3686
3736
|
|
|
3687
3737
|
// draw the particle
|
|
3688
|
-
this.additive && setBlendMode(
|
|
3738
|
+
this.additive && setBlendMode(true);
|
|
3689
3739
|
|
|
3690
3740
|
let pos = this.pos, angle = this.angle;
|
|
3691
3741
|
if (this.localSpaceEmitter)
|
|
@@ -3775,7 +3825,7 @@ class Medal
|
|
|
3775
3825
|
* @param {Number} id - The unique identifier of the medal
|
|
3776
3826
|
* @param {String} name - Name of the medal
|
|
3777
3827
|
* @param {String} [description] - Description of the medal
|
|
3778
|
-
* @param {String} [icon
|
|
3828
|
+
* @param {String} [icon] - Icon for the medal
|
|
3779
3829
|
* @param {String} [src] - Image location for the medal
|
|
3780
3830
|
*/
|
|
3781
3831
|
constructor(id, name, description='', icon='🏆', src)
|
|
@@ -3798,14 +3848,14 @@ class Medal
|
|
|
3798
3848
|
return;
|
|
3799
3849
|
|
|
3800
3850
|
// save the medal
|
|
3801
|
-
ASSERT(medalsSaveName
|
|
3851
|
+
ASSERT(medalsSaveName, 'save name must be set');
|
|
3802
3852
|
localStorage[this.storageKey()] = this.unlocked = 1;
|
|
3803
3853
|
medalsDisplayQueue.push(this);
|
|
3804
3854
|
newgrounds && newgrounds.unlockMedal(this.id);
|
|
3805
3855
|
}
|
|
3806
3856
|
|
|
3807
3857
|
/** Render a medal
|
|
3808
|
-
* @param {Number} [hidePercent
|
|
3858
|
+
* @param {Number} [hidePercent] - How much to slide the medal off screen
|
|
3809
3859
|
*/
|
|
3810
3860
|
render(hidePercent=0)
|
|
3811
3861
|
{
|
|
@@ -3817,25 +3867,25 @@ class Medal
|
|
|
3817
3867
|
// draw containing rect and clip to that region
|
|
3818
3868
|
context.save();
|
|
3819
3869
|
context.beginPath();
|
|
3820
|
-
context.fillStyle =
|
|
3821
|
-
context.strokeStyle =
|
|
3870
|
+
context.fillStyle = rgb(.9,.9,.9).toString();
|
|
3871
|
+
context.strokeStyle = rgb(0,0,0).toString();
|
|
3822
3872
|
context.lineWidth = 3;
|
|
3823
|
-
context.
|
|
3873
|
+
context.rect(x, y, width, medalDisplaySize.y);
|
|
3874
|
+
context.fill();
|
|
3824
3875
|
context.stroke();
|
|
3825
3876
|
context.clip();
|
|
3826
3877
|
|
|
3827
3878
|
// draw the icon and text
|
|
3828
3879
|
this.renderIcon(vec2(x+15+medalDisplayIconSize/2, y+medalDisplaySize.y/2));
|
|
3829
3880
|
const pos = vec2(x+medalDisplayIconSize+30, y+28);
|
|
3830
|
-
drawTextScreen(this.name, pos, 38, new Color(0,0,0), 0,
|
|
3881
|
+
drawTextScreen(this.name, pos, 38, new Color(0,0,0), 0, undefined, 'left');
|
|
3831
3882
|
pos.y += 32;
|
|
3832
|
-
drawTextScreen(this.description, pos, 24, new Color(0,0,0), 0,
|
|
3883
|
+
drawTextScreen(this.description, pos, 24, new Color(0,0,0), 0, undefined, 'left');
|
|
3833
3884
|
context.restore();
|
|
3834
3885
|
}
|
|
3835
3886
|
|
|
3836
3887
|
/** Render the icon for a medal
|
|
3837
|
-
* @param {
|
|
3838
|
-
* @param {Number} y - Screen space Y position
|
|
3888
|
+
* @param {Vector2} pos - Screen space position
|
|
3839
3889
|
* @param {Number} [size=medalDisplayIconSize] - Screen space size
|
|
3840
3890
|
*/
|
|
3841
3891
|
renderIcon(pos, size=medalDisplayIconSize)
|
|
@@ -3863,7 +3913,10 @@ function medalsRender()
|
|
|
3863
3913
|
if (!medalsDisplayTimeLast)
|
|
3864
3914
|
medalsDisplayTimeLast = timeReal;
|
|
3865
3915
|
else if (time > medalDisplayTime)
|
|
3866
|
-
|
|
3916
|
+
{
|
|
3917
|
+
medalsDisplayTimeLast = 0;
|
|
3918
|
+
medalsDisplayQueue.shift();
|
|
3919
|
+
}
|
|
3867
3920
|
else
|
|
3868
3921
|
{
|
|
3869
3922
|
// slide on/off medals
|
|
@@ -3903,8 +3956,8 @@ class Newgrounds
|
|
|
3903
3956
|
* @param {Object} [cryptoJS] - An instance of CryptoJS, if there is a cipher */
|
|
3904
3957
|
constructor(app_id, cipher, cryptoJS)
|
|
3905
3958
|
{
|
|
3906
|
-
ASSERT(!newgrounds && app_id
|
|
3907
|
-
ASSERT(!cipher || cryptoJS
|
|
3959
|
+
ASSERT(!newgrounds && app_id>0, 'there can only be one newgrounds object');
|
|
3960
|
+
ASSERT(!cipher || cryptoJS, 'must provide cryptojs if there is a cipher');
|
|
3908
3961
|
|
|
3909
3962
|
this.app_id = app_id;
|
|
3910
3963
|
this.cipher = cipher;
|
|
@@ -3947,39 +4000,39 @@ class Newgrounds
|
|
|
3947
4000
|
debugMedals && console.log(this.scoreboards);
|
|
3948
4001
|
|
|
3949
4002
|
const keepAliveMS = 5 * 60 * 1e3;
|
|
3950
|
-
setInterval(()=>this.call('Gateway.ping', 0,
|
|
4003
|
+
setInterval(()=>this.call('Gateway.ping', 0, true), keepAliveMS);
|
|
3951
4004
|
}
|
|
3952
4005
|
|
|
3953
4006
|
/** Send message to unlock a medal by id
|
|
3954
4007
|
* @param {Number} id - The medal id */
|
|
3955
|
-
unlockMedal(id) { return this.call('Medal.unlock', {'id':id},
|
|
4008
|
+
unlockMedal(id) { return this.call('Medal.unlock', {'id':id}, true); }
|
|
3956
4009
|
|
|
3957
4010
|
/** Send message to post score
|
|
3958
4011
|
* @param {Number} id - The scoreboard id
|
|
3959
4012
|
* @param {Number} value - The score value */
|
|
3960
|
-
postScore(id, value) { return this.call('ScoreBoard.postScore', {'id':id, 'value':value},
|
|
4013
|
+
postScore(id, value) { return this.call('ScoreBoard.postScore', {'id':id, 'value':value}, true); }
|
|
3961
4014
|
|
|
3962
4015
|
/** Get scores from a scoreboard
|
|
3963
|
-
* @param {Number} id
|
|
3964
|
-
* @param {String} [user
|
|
3965
|
-
* @param {Number} [social
|
|
3966
|
-
* @param {Number} [skip
|
|
3967
|
-
* @param {Number} [limit
|
|
3968
|
-
* @return {Object}
|
|
4016
|
+
* @param {Number} id - The scoreboard id
|
|
4017
|
+
* @param {String} [user] - A user's id or name
|
|
4018
|
+
* @param {Number} [social] - If true, only social scores will be loaded
|
|
4019
|
+
* @param {Number} [skip] - Number of scores to skip before start
|
|
4020
|
+
* @param {Number} [limit] - Number of scores to include in the list
|
|
4021
|
+
* @return {Object} - The response JSON object
|
|
3969
4022
|
*/
|
|
3970
|
-
getScores(id, user
|
|
4023
|
+
getScores(id, user, social=0, skip=0, limit=10)
|
|
3971
4024
|
{ return this.call('ScoreBoard.getScores', {'id':id, 'user':user, 'social':social, 'skip':skip, 'limit':limit}); }
|
|
3972
4025
|
|
|
3973
4026
|
/** Send message to log a view */
|
|
3974
|
-
logView() { return this.call('App.logView', {'host':this.host},
|
|
4027
|
+
logView() { return this.call('App.logView', {'host':this.host}, true); }
|
|
3975
4028
|
|
|
3976
4029
|
/** Send a message to call a component of the Newgrounds API
|
|
3977
|
-
* @param {String} component
|
|
3978
|
-
* @param {Object} [parameters
|
|
3979
|
-
* @param {Boolean} [async
|
|
3980
|
-
* @return {Object}
|
|
4030
|
+
* @param {String} component - Name of the component
|
|
4031
|
+
* @param {Object} [parameters] - Parameters to use for call
|
|
4032
|
+
* @param {Boolean} [async] - If true, don't wait for response before continuing
|
|
4033
|
+
* @return {Object} - The response JSON object
|
|
3981
4034
|
*/
|
|
3982
|
-
call(component, parameters
|
|
4035
|
+
call(component, parameters, async=false)
|
|
3983
4036
|
{
|
|
3984
4037
|
const call = {'component':component, 'parameters':parameters};
|
|
3985
4038
|
if (this.cipher)
|
|
@@ -4014,7 +4067,7 @@ class Newgrounds
|
|
|
4014
4067
|
return xmlHttp.responseText && JSON.parse(xmlHttp.responseText);
|
|
4015
4068
|
}
|
|
4016
4069
|
}
|
|
4017
|
-
/**
|
|
4070
|
+
/**
|
|
4018
4071
|
* LittleJS WebGL Interface
|
|
4019
4072
|
* - All webgl used by the engine is wrapped up here
|
|
4020
4073
|
* - For normal stuff you won't need to see or call anything in this file
|
|
@@ -4033,13 +4086,13 @@ class Newgrounds
|
|
|
4033
4086
|
* @memberof WebGL */
|
|
4034
4087
|
let glCanvas;
|
|
4035
4088
|
|
|
4036
|
-
/** 2d context for glCanvas
|
|
4037
|
-
* @type {
|
|
4089
|
+
/** 2d context for glCanvas
|
|
4090
|
+
* @type {WebGL2RenderingContext}
|
|
4038
4091
|
* @memberof WebGL */
|
|
4039
4092
|
let glContext;
|
|
4040
4093
|
|
|
4041
4094
|
// WebGL internal variables not exposed to documentation
|
|
4042
|
-
let
|
|
4095
|
+
let glShader, glActiveTexture, glArrayBuffer, glGeometryBuffer, glPositionData, glColorData, glInstanceCount, glAdditive, glBatchAdditive;
|
|
4043
4096
|
|
|
4044
4097
|
///////////////////////////////////////////////////////////////////////////////
|
|
4045
4098
|
|
|
@@ -4055,73 +4108,89 @@ function glInit()
|
|
|
4055
4108
|
|
|
4056
4109
|
// setup vertex and fragment shaders
|
|
4057
4110
|
glShader = glCreateProgram(
|
|
4058
|
-
'#version 300 es\n' +
|
|
4059
|
-
'precision highp float;'+
|
|
4060
|
-
'uniform mat4 m;'+
|
|
4061
|
-
'in
|
|
4062
|
-
'
|
|
4063
|
-
'
|
|
4064
|
-
'
|
|
4065
|
-
'
|
|
4066
|
-
'
|
|
4111
|
+
'#version 300 es\n' + // specify GLSL ES version
|
|
4112
|
+
'precision highp float;'+ // use highp for better accuracy
|
|
4113
|
+
'uniform mat4 m;'+ // transform matrix
|
|
4114
|
+
'in vec2 g;'+ // geometry
|
|
4115
|
+
'in vec4 p,u,c,a;'+ // position/size, uvs, color, additiveColor
|
|
4116
|
+
'in float r;'+ // rotation
|
|
4117
|
+
'out vec2 v;'+ // return uv, color, additiveColor
|
|
4118
|
+
'out vec4 d,e;'+ // return uv, color, additiveColor
|
|
4119
|
+
'void main(){'+ // shader entry point
|
|
4120
|
+
'vec2 s=(g-.5)*p.zw;'+ // get size offset
|
|
4121
|
+
'gl_Position=m*vec4(p.xy+s*cos(r)-vec2(-s.y,s)*sin(r),1,1);'+ // transform position
|
|
4122
|
+
'v=mix(u.xw,u.zy,g);'+ // pass uv to fragment shader
|
|
4123
|
+
'd=c;e=a;'+ // pass colors to fragment shader
|
|
4124
|
+
'}' // end of shader
|
|
4067
4125
|
,
|
|
4068
|
-
'#version 300 es\n' +
|
|
4069
|
-
'precision highp float;'+
|
|
4070
|
-
'in
|
|
4071
|
-
'
|
|
4072
|
-
'
|
|
4073
|
-
'
|
|
4074
|
-
'
|
|
4075
|
-
'
|
|
4126
|
+
'#version 300 es\n' + // specify GLSL ES version
|
|
4127
|
+
'precision highp float;'+ // use highp for better accuracy
|
|
4128
|
+
'in vec2 v;'+ // uv
|
|
4129
|
+
'in vec4 d,e;'+ // color, additiveColor
|
|
4130
|
+
'uniform sampler2D s;'+ // texture
|
|
4131
|
+
'out vec4 c;'+ // out color
|
|
4132
|
+
'void main(){'+ // shader entry point
|
|
4133
|
+
'c=texture(s,v)*d+e;'+ // modulate texture by color plus additive
|
|
4134
|
+
'}' // end of shader
|
|
4076
4135
|
);
|
|
4077
4136
|
|
|
4078
4137
|
// init buffers
|
|
4079
|
-
|
|
4080
|
-
glPositionData = new Float32Array(
|
|
4081
|
-
glColorData = new Uint32Array(
|
|
4138
|
+
const glInstanceData = new ArrayBuffer(gl_INSTANCE_BUFFER_SIZE);
|
|
4139
|
+
glPositionData = new Float32Array(glInstanceData);
|
|
4140
|
+
glColorData = new Uint32Array(glInstanceData);
|
|
4082
4141
|
glArrayBuffer = glContext.createBuffer();
|
|
4083
|
-
|
|
4142
|
+
glGeometryBuffer = glContext.createBuffer();
|
|
4143
|
+
|
|
4144
|
+
// create the geometry buffer, triangle strip square
|
|
4145
|
+
const geometry = new Float32Array([glInstanceCount=0,0,1,0,0,1,1,1]);
|
|
4146
|
+
glContext.bindBuffer(gl_ARRAY_BUFFER, glGeometryBuffer);
|
|
4147
|
+
glContext.bufferData(gl_ARRAY_BUFFER, geometry, gl_STATIC_DRAW);
|
|
4084
4148
|
}
|
|
4085
4149
|
|
|
4086
4150
|
// Setup render each frame, called automatically by engine
|
|
4087
4151
|
function glPreRender()
|
|
4088
4152
|
{
|
|
4089
4153
|
// clear and set to same size as main canvas
|
|
4090
|
-
glContext.viewport(0, 0, glCanvas.width
|
|
4154
|
+
glContext.viewport(0, 0, glCanvas.width=mainCanvas.width, glCanvas.height=mainCanvas.height);
|
|
4091
4155
|
glContext.clear(gl_COLOR_BUFFER_BIT);
|
|
4092
4156
|
|
|
4093
4157
|
// set up the shader
|
|
4094
4158
|
glContext.useProgram(glShader);
|
|
4095
4159
|
glContext.activeTexture(gl_TEXTURE0);
|
|
4096
4160
|
glContext.bindTexture(gl_TEXTURE_2D, glActiveTexture = textureInfos[0].glTexture);
|
|
4097
|
-
|
|
4098
|
-
glContext.bufferData(gl_ARRAY_BUFFER, gl_VERTEX_BUFFER_SIZE, gl_DYNAMIC_DRAW);
|
|
4099
|
-
glAdditive = 0;
|
|
4100
|
-
|
|
4161
|
+
|
|
4101
4162
|
// set vertex attributes
|
|
4102
|
-
let offset = 0;
|
|
4103
|
-
|
|
4163
|
+
let offset = glAdditive = glBatchAdditive = 0;
|
|
4164
|
+
let initVertexAttribArray = (name, type, typeSize, size)=>
|
|
4104
4165
|
{
|
|
4105
4166
|
const location = glContext.getAttribLocation(glShader, name);
|
|
4167
|
+
const stride = typeSize && gl_INSTANCE_BYTE_STRIDE; // only if not geometry
|
|
4168
|
+
const divisor = typeSize && 1; // only if not geometry
|
|
4169
|
+
const normalize = typeSize==1; // only if color
|
|
4106
4170
|
glContext.enableVertexAttribArray(location);
|
|
4107
|
-
glContext.vertexAttribPointer(location, size, type, normalize,
|
|
4171
|
+
glContext.vertexAttribPointer(location, size, type, normalize, stride, offset);
|
|
4172
|
+
glContext.vertexAttribDivisor(location, divisor);
|
|
4108
4173
|
offset += size*typeSize;
|
|
4109
4174
|
}
|
|
4110
|
-
|
|
4111
|
-
initVertexAttribArray('
|
|
4112
|
-
|
|
4175
|
+
glContext.bindBuffer(gl_ARRAY_BUFFER, glGeometryBuffer);
|
|
4176
|
+
initVertexAttribArray('g', gl_FLOAT, 0, 2); // geometry
|
|
4177
|
+
glContext.bindBuffer(gl_ARRAY_BUFFER, glArrayBuffer);
|
|
4178
|
+
glContext.bufferData(gl_ARRAY_BUFFER, gl_INSTANCE_BUFFER_SIZE, gl_DYNAMIC_DRAW);
|
|
4179
|
+
initVertexAttribArray('p', gl_FLOAT, 4, 4); // position & size
|
|
4180
|
+
initVertexAttribArray('u', gl_FLOAT, 4, 4); // texture coords
|
|
4181
|
+
initVertexAttribArray('c', gl_UNSIGNED_BYTE, 1, 4); // color
|
|
4182
|
+
initVertexAttribArray('a', gl_UNSIGNED_BYTE, 1, 4); // additiveColor
|
|
4183
|
+
initVertexAttribArray('r', gl_FLOAT, 4, 1); // rotation
|
|
4113
4184
|
|
|
4114
4185
|
// build the transform matrix
|
|
4115
|
-
const
|
|
4116
|
-
const
|
|
4117
|
-
|
|
4118
|
-
const cy = -1 - sy*cameraPos.y;
|
|
4119
|
-
glContext.uniformMatrix4fv(glContext.getUniformLocation(glShader, 'm'), 0,
|
|
4186
|
+
const s = vec2(2*cameraScale).divide(mainCanvasSize);
|
|
4187
|
+
const p = vec2(-1).subtract(cameraPos.multiply(s));
|
|
4188
|
+
glContext.uniformMatrix4fv(glContext.getUniformLocation(glShader, 'm'), false,
|
|
4120
4189
|
new Float32Array([
|
|
4121
|
-
|
|
4122
|
-
|
|
4123
|
-
|
|
4124
|
-
|
|
4190
|
+
s.x, 0, 0, 0,
|
|
4191
|
+
0, s.y, 0, 0,
|
|
4192
|
+
1, 1, 1, 1,
|
|
4193
|
+
p.x, p.y, 0, 0
|
|
4125
4194
|
])
|
|
4126
4195
|
);
|
|
4127
4196
|
}
|
|
@@ -4142,7 +4211,7 @@ function glSetTexture(texture)
|
|
|
4142
4211
|
|
|
4143
4212
|
/** Compile WebGL shader of the given type, will throw errors if in debug mode
|
|
4144
4213
|
* @param {String} source
|
|
4145
|
-
* @param
|
|
4214
|
+
* @param {Number} type
|
|
4146
4215
|
* @return {WebGLShader}
|
|
4147
4216
|
* @memberof WebGL */
|
|
4148
4217
|
function glCompileShader(source, type)
|
|
@@ -4159,8 +4228,8 @@ function glCompileShader(source, type)
|
|
|
4159
4228
|
}
|
|
4160
4229
|
|
|
4161
4230
|
/** Create WebGL program with given shaders
|
|
4162
|
-
* @param {
|
|
4163
|
-
* @param {
|
|
4231
|
+
* @param {String} vsSource
|
|
4232
|
+
* @param {String} fsSource
|
|
4164
4233
|
* @return {WebGLProgram}
|
|
4165
4234
|
* @memberof WebGL */
|
|
4166
4235
|
function glCreateProgram(vsSource, fsSource)
|
|
@@ -4178,7 +4247,7 @@ function glCreateProgram(vsSource, fsSource)
|
|
|
4178
4247
|
}
|
|
4179
4248
|
|
|
4180
4249
|
/** Create WebGL texture from an image and init the texture settings
|
|
4181
|
-
* @param {
|
|
4250
|
+
* @param {HTMLImageElement} image
|
|
4182
4251
|
* @return {WebGLTexture}
|
|
4183
4252
|
* @memberof WebGL */
|
|
4184
4253
|
function glCreateTexture(image)
|
|
@@ -4188,7 +4257,7 @@ function glCreateTexture(image)
|
|
|
4188
4257
|
glContext.bindTexture(gl_TEXTURE_2D, texture);
|
|
4189
4258
|
if (image)
|
|
4190
4259
|
glContext.texImage2D(gl_TEXTURE_2D, 0, gl_RGBA, gl_RGBA, gl_UNSIGNED_BYTE, image);
|
|
4191
|
-
|
|
4260
|
+
|
|
4192
4261
|
// use point filtering for pixelated rendering
|
|
4193
4262
|
const filter = canvasPixelated ? gl_NEAREST : gl_LINEAR;
|
|
4194
4263
|
glContext.texParameteri(gl_TEXTURE_2D, gl_TEXTURE_MIN_FILTER, filter);
|
|
@@ -4203,29 +4272,31 @@ function glCreateTexture(image)
|
|
|
4203
4272
|
* @memberof WebGL */
|
|
4204
4273
|
function glFlush()
|
|
4205
4274
|
{
|
|
4206
|
-
if (!
|
|
4275
|
+
if (!glInstanceCount) return;
|
|
4207
4276
|
|
|
4208
4277
|
const destBlend = glBatchAdditive ? gl_ONE : gl_ONE_MINUS_SRC_ALPHA;
|
|
4209
4278
|
glContext.blendFuncSeparate(gl_SRC_ALPHA, destBlend, gl_ONE, destBlend);
|
|
4210
4279
|
glContext.enable(gl_BLEND);
|
|
4211
4280
|
|
|
4212
4281
|
// draw all the sprites in the batch and reset the buffer
|
|
4213
|
-
glContext.bufferSubData(gl_ARRAY_BUFFER, 0,
|
|
4214
|
-
glContext.
|
|
4215
|
-
|
|
4282
|
+
glContext.bufferSubData(gl_ARRAY_BUFFER, 0, glPositionData);
|
|
4283
|
+
glContext.drawArraysInstanced(gl_TRIANGLE_STRIP, 0, 4, glInstanceCount);
|
|
4284
|
+
if (showWatermark)
|
|
4285
|
+
drawCount += glInstanceCount;
|
|
4286
|
+
glInstanceCount = 0;
|
|
4216
4287
|
glBatchAdditive = glAdditive;
|
|
4217
4288
|
}
|
|
4218
4289
|
|
|
4219
4290
|
/** Draw any sprites still in the buffer, copy to main canvas and clear
|
|
4220
4291
|
* @param {CanvasRenderingContext2D} context
|
|
4221
|
-
* @param {Boolean} [forceDraw
|
|
4292
|
+
* @param {Boolean} [forceDraw]
|
|
4222
4293
|
* @memberof WebGL */
|
|
4223
|
-
function glCopyToContext(context, forceDraw)
|
|
4294
|
+
function glCopyToContext(context, forceDraw=false)
|
|
4224
4295
|
{
|
|
4225
|
-
if (!
|
|
4226
|
-
|
|
4296
|
+
if (!glInstanceCount && !forceDraw) return;
|
|
4297
|
+
|
|
4227
4298
|
glFlush();
|
|
4228
|
-
|
|
4299
|
+
|
|
4229
4300
|
// do not draw in overlay mode because the canvas is visible
|
|
4230
4301
|
if (!glOverlay || forceDraw)
|
|
4231
4302
|
context.drawImage(glCanvas, 0, 0);
|
|
@@ -4247,60 +4318,22 @@ function glCopyToContext(context, forceDraw)
|
|
|
4247
4318
|
function glDraw(x, y, sizeX, sizeY, angle, uv0X, uv0Y, uv1X, uv1Y, rgba, rgbaAdditive=0)
|
|
4248
4319
|
{
|
|
4249
4320
|
// flush if there is not enough room or if different blend mode
|
|
4250
|
-
|
|
4251
|
-
if (glBatchCount >= gl_MAX_BATCH-vertCount || glBatchAdditive != glAdditive)
|
|
4321
|
+
if (glInstanceCount >= gl_MAX_INSTANCES-1 || glBatchAdditive != glAdditive)
|
|
4252
4322
|
glFlush();
|
|
4253
4323
|
|
|
4254
|
-
|
|
4255
|
-
|
|
4256
|
-
|
|
4257
|
-
|
|
4258
|
-
[
|
|
4259
|
-
|
|
4260
|
-
|
|
4261
|
-
|
|
4262
|
-
|
|
4263
|
-
];
|
|
4264
|
-
|
|
4265
|
-
|
|
4266
|
-
|
|
4267
|
-
{
|
|
4268
|
-
const j = clamp(i-1, 0, 3)*4; // degenerate tri at ends
|
|
4269
|
-
glPositionData[offset++] = positionData[j+0];
|
|
4270
|
-
glPositionData[offset++] = positionData[j+1];
|
|
4271
|
-
glPositionData[offset++] = positionData[j+2];
|
|
4272
|
-
glPositionData[offset++] = positionData[j+3];
|
|
4273
|
-
glColorData[offset++] = rgba;
|
|
4274
|
-
glColorData[offset++] = rgbaAdditive;
|
|
4275
|
-
}
|
|
4276
|
-
glBatchCount += vertCount;
|
|
4277
|
-
}
|
|
4278
|
-
|
|
4279
|
-
/** Add a convex polygon to the gl draw list
|
|
4280
|
-
* @param {Array} points - Array of Vector2 points
|
|
4281
|
-
* @param {Number} rgba - Color of the polygon
|
|
4282
|
-
* @memberof WebGL */
|
|
4283
|
-
function glDrawPoints(points, rgba)
|
|
4284
|
-
{
|
|
4285
|
-
// flush if there is not enough room or if different blend mode
|
|
4286
|
-
const vertCount = points.length + 2;
|
|
4287
|
-
if (glBatchCount >= gl_MAX_BATCH-vertCount || glBatchAdditive != glAdditive)
|
|
4288
|
-
glFlush();
|
|
4289
|
-
|
|
4290
|
-
// setup triangle strip from list of points
|
|
4291
|
-
for(let i = vertCount, offset = glBatchCount * gl_INDICIES_PER_VERT; i--;)
|
|
4292
|
-
{
|
|
4293
|
-
const j = clamp(i-1, 0, vertCount-3); // degenerate tri at ends
|
|
4294
|
-
const h = j>>1;
|
|
4295
|
-
const point = points[j%2? h : vertCount-3-h];
|
|
4296
|
-
glPositionData[offset++] = point.x;
|
|
4297
|
-
glPositionData[offset++] = point.y;
|
|
4298
|
-
glPositionData[offset++] = 0; // uvx
|
|
4299
|
-
glPositionData[offset++] = 0; // uvy
|
|
4300
|
-
glColorData[offset++] = 0; // nothing to tint
|
|
4301
|
-
glColorData[offset++] = rgba; // apply rgba via additive
|
|
4302
|
-
}
|
|
4303
|
-
glBatchCount += vertCount;
|
|
4324
|
+
let offset = glInstanceCount * gl_INDICIES_PER_INSTANCE;
|
|
4325
|
+
glPositionData[offset++] = x;
|
|
4326
|
+
glPositionData[offset++] = y;
|
|
4327
|
+
glPositionData[offset++] = sizeX;
|
|
4328
|
+
glPositionData[offset++] = sizeY;
|
|
4329
|
+
glPositionData[offset++] = uv0X;
|
|
4330
|
+
glPositionData[offset++] = uv0Y;
|
|
4331
|
+
glPositionData[offset++] = uv1X;
|
|
4332
|
+
glPositionData[offset++] = uv1Y;
|
|
4333
|
+
glColorData[offset++] = rgba;
|
|
4334
|
+
glColorData[offset++] = rgbaAdditive;
|
|
4335
|
+
glPositionData[offset++] = angle;
|
|
4336
|
+
glInstanceCount++;
|
|
4304
4337
|
}
|
|
4305
4338
|
|
|
4306
4339
|
///////////////////////////////////////////////////////////////////////////////
|
|
@@ -4314,7 +4347,7 @@ let glPostShader, glPostArrayBuffer, glPostTexture, glPostIncludeOverlay;
|
|
|
4314
4347
|
* @memberof WebGL */
|
|
4315
4348
|
function glInitPostProcess(shaderCode, includeOverlay)
|
|
4316
4349
|
{
|
|
4317
|
-
ASSERT(!glPostShader
|
|
4350
|
+
ASSERT(!glPostShader, 'can only have 1 post effects shader');
|
|
4318
4351
|
|
|
4319
4352
|
if (!shaderCode) // default shader pass through
|
|
4320
4353
|
shaderCode = 'void mainImage(out vec4 c,vec2 p){c=texture(iChannel0,p/iResolution.xy);}';
|
|
@@ -4325,14 +4358,14 @@ function glInitPostProcess(shaderCode, includeOverlay)
|
|
|
4325
4358
|
'precision highp float;'+ // use highp for better accuracy
|
|
4326
4359
|
'in vec2 p;'+ // position
|
|
4327
4360
|
'void main(){'+ // shader entry point
|
|
4328
|
-
'gl_Position=vec4(p
|
|
4361
|
+
'gl_Position=vec4(p+p-1.,1,1);'+ // set position
|
|
4329
4362
|
'}' // end of shader
|
|
4330
4363
|
,
|
|
4331
4364
|
'#version 300 es\n' + // specify GLSL ES version
|
|
4332
4365
|
'precision highp float;'+ // use highp for better accuracy
|
|
4333
4366
|
'uniform sampler2D iChannel0;'+ // input texture
|
|
4334
4367
|
'uniform vec3 iResolution;'+ // size of output texture
|
|
4335
|
-
'uniform float iTime;'+ // time
|
|
4368
|
+
'uniform float iTime;'+ // time
|
|
4336
4369
|
'out vec4 c;'+ // out color
|
|
4337
4370
|
'\n' + shaderCode + '\n'+ // insert custom shader code
|
|
4338
4371
|
'void main(){'+ // shader entry point
|
|
@@ -4343,7 +4376,7 @@ function glInitPostProcess(shaderCode, includeOverlay)
|
|
|
4343
4376
|
|
|
4344
4377
|
// create buffer and texture
|
|
4345
4378
|
glPostArrayBuffer = glContext.createBuffer();
|
|
4346
|
-
glPostTexture = glCreateTexture();
|
|
4379
|
+
glPostTexture = glCreateTexture(undefined);
|
|
4347
4380
|
glPostIncludeOverlay = includeOverlay;
|
|
4348
4381
|
|
|
4349
4382
|
// hide the original 2d canvas
|
|
@@ -4379,10 +4412,9 @@ function glRenderPostProcess()
|
|
|
4379
4412
|
|
|
4380
4413
|
// setup shader program to draw one triangle
|
|
4381
4414
|
glContext.useProgram(glPostShader);
|
|
4415
|
+
glContext.bindBuffer(gl_ARRAY_BUFFER, glGeometryBuffer);
|
|
4416
|
+
glContext.pixelStorei(gl_UNPACK_FLIP_Y_WEBGL, 1);
|
|
4382
4417
|
glContext.disable(gl_BLEND);
|
|
4383
|
-
glContext.bindBuffer(gl_ARRAY_BUFFER, glPostArrayBuffer);
|
|
4384
|
-
glContext.bufferData(gl_ARRAY_BUFFER, new Float32Array([-3,1,1,-3,1,1]), gl_STATIC_DRAW);
|
|
4385
|
-
glContext.pixelStorei(gl_UNPACK_FLIP_Y_WEBGL, true);
|
|
4386
4418
|
|
|
4387
4419
|
// set textures, pass in the 2d canvas and gl canvas in separate texture channels
|
|
4388
4420
|
glContext.activeTexture(gl_TEXTURE0);
|
|
@@ -4393,19 +4425,19 @@ function glRenderPostProcess()
|
|
|
4393
4425
|
const vertexByteStride = 8;
|
|
4394
4426
|
const pLocation = glContext.getAttribLocation(glPostShader, 'p');
|
|
4395
4427
|
glContext.enableVertexAttribArray(pLocation);
|
|
4396
|
-
glContext.vertexAttribPointer(pLocation, 2, gl_FLOAT,
|
|
4428
|
+
glContext.vertexAttribPointer(pLocation, 2, gl_FLOAT, false, vertexByteStride, 0);
|
|
4397
4429
|
|
|
4398
4430
|
// set uniforms and draw
|
|
4399
4431
|
const uniformLocation = (name)=>glContext.getUniformLocation(glPostShader, name);
|
|
4400
4432
|
glContext.uniform1i(uniformLocation('iChannel0'), 0);
|
|
4401
4433
|
glContext.uniform1f(uniformLocation('iTime'), time);
|
|
4402
4434
|
glContext.uniform3f(uniformLocation('iResolution'), mainCanvas.width, mainCanvas.height, 1);
|
|
4403
|
-
glContext.drawArrays(gl_TRIANGLE_STRIP, 0,
|
|
4435
|
+
glContext.drawArrays(gl_TRIANGLE_STRIP, 0, 4);
|
|
4404
4436
|
}
|
|
4405
4437
|
|
|
4406
4438
|
///////////////////////////////////////////////////////////////////////////////
|
|
4407
4439
|
// store gl constants as integers so their name doesn't use space in minifed
|
|
4408
|
-
const
|
|
4440
|
+
const
|
|
4409
4441
|
gl_ONE = 1,
|
|
4410
4442
|
gl_TRIANGLE_STRIP = 5,
|
|
4411
4443
|
gl_SRC_ALPHA = 770,
|
|
@@ -4427,17 +4459,17 @@ gl_TEXTURE0 = 33984,
|
|
|
4427
4459
|
gl_ARRAY_BUFFER = 34962,
|
|
4428
4460
|
gl_STATIC_DRAW = 35044,
|
|
4429
4461
|
gl_DYNAMIC_DRAW = 35048,
|
|
4430
|
-
gl_FRAGMENT_SHADER = 35632,
|
|
4462
|
+
gl_FRAGMENT_SHADER = 35632,
|
|
4431
4463
|
gl_VERTEX_SHADER = 35633,
|
|
4432
4464
|
gl_COMPILE_STATUS = 35713,
|
|
4433
4465
|
gl_LINK_STATUS = 35714,
|
|
4434
4466
|
gl_UNPACK_FLIP_Y_WEBGL = 37440,
|
|
4435
4467
|
|
|
4436
4468
|
// constants for batch rendering
|
|
4437
|
-
|
|
4438
|
-
|
|
4439
|
-
|
|
4440
|
-
|
|
4469
|
+
gl_INDICIES_PER_INSTANCE = 11,
|
|
4470
|
+
gl_MAX_INSTANCES = 1e4,
|
|
4471
|
+
gl_INSTANCE_BYTE_STRIDE = gl_INDICIES_PER_INSTANCE * 4, // 11 * 4
|
|
4472
|
+
gl_INSTANCE_BUFFER_SIZE = gl_MAX_INSTANCES * gl_INSTANCE_BYTE_STRIDE;
|
|
4441
4473
|
/**
|
|
4442
4474
|
* LittleJS - The Tiny JavaScript Game Engine That Can!
|
|
4443
4475
|
* MIT License - Copyright 2021 Frank Force
|
|
@@ -4470,7 +4502,7 @@ const engineName = 'LittleJS';
|
|
|
4470
4502
|
* @type {String}
|
|
4471
4503
|
* @default
|
|
4472
4504
|
* @memberof Engine */
|
|
4473
|
-
const engineVersion = '1.
|
|
4505
|
+
const engineVersion = '1.9.0';
|
|
4474
4506
|
|
|
4475
4507
|
/** Frames per second to update objects
|
|
4476
4508
|
* @type {Number}
|
|
@@ -4511,14 +4543,14 @@ let timeReal = 0;
|
|
|
4511
4543
|
|
|
4512
4544
|
/** Is the game paused? Causes time and objects to not be updated
|
|
4513
4545
|
* @type {Boolean}
|
|
4514
|
-
* @default
|
|
4546
|
+
* @default false
|
|
4515
4547
|
* @memberof Engine */
|
|
4516
|
-
let paused =
|
|
4548
|
+
let paused = false;
|
|
4517
4549
|
|
|
4518
4550
|
/** Set if game is paused
|
|
4519
|
-
* @param {Boolean}
|
|
4551
|
+
* @param {Boolean} isPaused
|
|
4520
4552
|
* @memberof Engine */
|
|
4521
|
-
function setPaused(
|
|
4553
|
+
function setPaused(isPaused) { paused = isPaused; }
|
|
4522
4554
|
|
|
4523
4555
|
// Frame time tracking
|
|
4524
4556
|
let frameTimeLastMS = 0, frameTimeBufferMS = 0, averageFPS = 0;
|
|
@@ -4535,7 +4567,7 @@ let frameTimeLastMS = 0, frameTimeBufferMS = 0, averageFPS = 0;
|
|
|
4535
4567
|
* @memberof Engine */
|
|
4536
4568
|
function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRenderPost, imageSources=['tiles.png'])
|
|
4537
4569
|
{
|
|
4538
|
-
ASSERT(Array.isArray(imageSources)
|
|
4570
|
+
ASSERT(Array.isArray(imageSources), 'pass in images as array');
|
|
4539
4571
|
|
|
4540
4572
|
// internal update loop for engine
|
|
4541
4573
|
function engineUpdate(frameTimeMS=0)
|
|
@@ -4545,12 +4577,12 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
4545
4577
|
frameTimeLastMS = frameTimeMS;
|
|
4546
4578
|
if (debug || showWatermark)
|
|
4547
4579
|
averageFPS = lerp(.05, averageFPS, 1e3/(frameTimeDeltaMS||1));
|
|
4548
|
-
const debugSpeedUp = debug && keyIsDown(
|
|
4549
|
-
const debugSpeedDown = debug && keyIsDown(
|
|
4580
|
+
const debugSpeedUp = debug && keyIsDown('Equal'); // +
|
|
4581
|
+
const debugSpeedDown = debug && keyIsDown('Minus'); // -
|
|
4550
4582
|
if (debug) // +/- to speed/slow time
|
|
4551
4583
|
frameTimeDeltaMS *= debugSpeedUp ? 5 : debugSpeedDown ? .2 : 1;
|
|
4552
4584
|
timeReal += frameTimeDeltaMS / 1e3;
|
|
4553
|
-
frameTimeBufferMS +=
|
|
4585
|
+
frameTimeBufferMS += paused ? 0 : frameTimeDeltaMS;
|
|
4554
4586
|
if (!debugSpeedUp)
|
|
4555
4587
|
frameTimeBufferMS = min(frameTimeBufferMS, 50); // clamp incase of slow framerate
|
|
4556
4588
|
|
|
@@ -4660,7 +4692,7 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
4660
4692
|
'user-select:none;' + // prevent mobile hold to select
|
|
4661
4693
|
'-webkit-user-select:none;' + // compatibility for ios
|
|
4662
4694
|
'-webkit-touch-callout:none'; // compatibility for ios
|
|
4663
|
-
document.body.style = styleBody;
|
|
4695
|
+
document.body.style.cssText = styleBody;
|
|
4664
4696
|
document.body.appendChild(mainCanvas = document.createElement('canvas'));
|
|
4665
4697
|
mainContext = mainCanvas.getContext('2d');
|
|
4666
4698
|
|
|
@@ -4673,10 +4705,9 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
4673
4705
|
overlayContext = overlayCanvas.getContext('2d');
|
|
4674
4706
|
|
|
4675
4707
|
// set canvas style
|
|
4676
|
-
const styleCanvas =
|
|
4677
|
-
'position:absolute;' + // position
|
|
4708
|
+
const styleCanvas = 'position:absolute;' + // position
|
|
4678
4709
|
'top:50%;left:50%;transform:translate(-50%,-50%)'; // center
|
|
4679
|
-
(glCanvas||mainCanvas).style = mainCanvas.style = overlayCanvas.style = styleCanvas;
|
|
4710
|
+
(glCanvas||mainCanvas).style.cssText = mainCanvas.style.cssText = overlayCanvas.style.cssText = styleCanvas;
|
|
4680
4711
|
|
|
4681
4712
|
// create promises for loading images
|
|
4682
4713
|
const promises = imageSources.map((src, textureIndex)=>
|
|
@@ -4696,13 +4727,13 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
4696
4727
|
showSplashScreen && promises.push(new Promise(resolve =>
|
|
4697
4728
|
{
|
|
4698
4729
|
let t = 0;
|
|
4699
|
-
console.log(
|
|
4730
|
+
console.log(`${engineName} Engine v${engineVersion}`);
|
|
4700
4731
|
updateSplash();
|
|
4701
4732
|
function updateSplash()
|
|
4702
4733
|
{
|
|
4703
4734
|
clearInput();
|
|
4704
4735
|
drawEngineSplashScreen(t+=.01);
|
|
4705
|
-
t>1 ? resolve() : setTimeout(updateSplash,16);
|
|
4736
|
+
t>1 ? resolve() : setTimeout(updateSplash, 16);
|
|
4706
4737
|
}
|
|
4707
4738
|
}));
|
|
4708
4739
|
|
|
@@ -4763,7 +4794,7 @@ function engineObjectsDestroy()
|
|
|
4763
4794
|
|
|
4764
4795
|
/** Triggers a callback for each object within a given area
|
|
4765
4796
|
* @param {Vector2} [pos] - Center of test area
|
|
4766
|
-
* @param {Number} [size]
|
|
4797
|
+
* @param {Number|Vector2} [size] - Radius of circle if float, rectangle size if Vector2
|
|
4767
4798
|
* @param {Function} [callbackFunction] - Calls this function on every object that passes the test
|
|
4768
4799
|
* @param {Array} [objects=engineObjects] - List of objects to check
|
|
4769
4800
|
* @memberof Engine */
|
|
@@ -4774,7 +4805,7 @@ function engineObjectsCallback(pos, size, callbackFunction, objects=engineObject
|
|
|
4774
4805
|
for (const o of objects)
|
|
4775
4806
|
callbackFunction(o);
|
|
4776
4807
|
}
|
|
4777
|
-
else if (size
|
|
4808
|
+
else if (typeof size === 'object') // bounding box test
|
|
4778
4809
|
{
|
|
4779
4810
|
for (const o of objects)
|
|
4780
4811
|
isOverlapping(pos, size, o.pos, o.size) && callbackFunction(o);
|
|
@@ -4792,23 +4823,23 @@ function engineObjectsCallback(pos, size, callbackFunction, objects=engineObject
|
|
|
4792
4823
|
|
|
4793
4824
|
function drawEngineSplashScreen(t)
|
|
4794
4825
|
{
|
|
4795
|
-
const x =
|
|
4796
|
-
const w =
|
|
4797
|
-
const h =
|
|
4826
|
+
const x = overlayContext;
|
|
4827
|
+
const w = overlayCanvas.width = innerWidth;
|
|
4828
|
+
const h = overlayCanvas.height = innerHeight;
|
|
4829
|
+
|
|
4798
4830
|
{
|
|
4799
4831
|
// background
|
|
4800
4832
|
const p3 = percent(t, 1, .8);
|
|
4801
4833
|
const p4 = percent(t, 0, .5);
|
|
4802
4834
|
const g = x.createRadialGradient(w/2,h/2,0,w/2,h/2,Math.hypot(w,h)*.7);
|
|
4803
|
-
g.addColorStop(0,hsl(0,0,lerp(p4,0,p3/2),p3));
|
|
4804
|
-
g.addColorStop(1,hsl(0,0,0,p3));
|
|
4835
|
+
g.addColorStop(0,hsl(0,0,lerp(p4,0,p3/2),p3).toString());
|
|
4836
|
+
g.addColorStop(1,hsl(0,0,0,p3).toString());
|
|
4805
4837
|
x.save();
|
|
4806
4838
|
x.fillStyle = g;
|
|
4807
4839
|
x.fillRect(0,0,w,h);
|
|
4808
4840
|
}
|
|
4809
4841
|
|
|
4810
4842
|
// draw LittleJS logo...
|
|
4811
|
-
|
|
4812
4843
|
const rect = (X, Y, W, H, C)=>
|
|
4813
4844
|
{
|
|
4814
4845
|
x.beginPath();
|
|
@@ -4833,7 +4864,7 @@ function drawEngineSplashScreen(t)
|
|
|
4833
4864
|
C ? x.fill() : x.stroke();
|
|
4834
4865
|
};
|
|
4835
4866
|
const color = (c=0, l=0) =>
|
|
4836
|
-
hsl([.98,.3,.57,.14][c%4]-10,.8,[0,.3,.5,.8,.9][l]);
|
|
4867
|
+
hsl([.98,.3,.57,.14][c%4]-10,.8,[0,.3,.5,.8,.9][l]).toString();
|
|
4837
4868
|
const alpha = wave(1,1,t);
|
|
4838
4869
|
const p = percent(alpha, .1, .5);
|
|
4839
4870
|
|
|
@@ -4843,7 +4874,7 @@ function drawEngineSplashScreen(t)
|
|
|
4843
4874
|
x.scale(size,size);
|
|
4844
4875
|
x.translate(-40,-35);
|
|
4845
4876
|
x.lineJoin = x.lineCap = 'round';
|
|
4846
|
-
x.lineWidth = 1+p;
|
|
4877
|
+
x.lineWidth = .1 + p*1.9;
|
|
4847
4878
|
|
|
4848
4879
|
// drawing effect
|
|
4849
4880
|
const p2 = percent(alpha,.1,1);
|
|
@@ -4868,7 +4899,7 @@ function drawEngineSplashScreen(t)
|
|
|
4868
4899
|
|
|
4869
4900
|
// little stack
|
|
4870
4901
|
rect(37,14,9,6,color(3,2));
|
|
4871
|
-
rect(37,14,4,6,color(3,3));
|
|
4902
|
+
rect(37,14,4.5,6,color(3,3));
|
|
4872
4903
|
rect(37,14,9,6);
|
|
4873
4904
|
|
|
4874
4905
|
// big stack
|
|
@@ -4912,7 +4943,8 @@ function drawEngineSplashScreen(t)
|
|
|
4912
4943
|
x.lineTo(53+(1+i*2.9)*p,40);
|
|
4913
4944
|
x.lineTo(53+(4+i*3.5)*p,54);
|
|
4914
4945
|
x.fillStyle = color(0,i%2+2);
|
|
4915
|
-
x.fill()
|
|
4946
|
+
x.fill();
|
|
4947
|
+
i%2 && x.stroke();
|
|
4916
4948
|
}
|
|
4917
4949
|
|
|
4918
4950
|
// wheels
|
|
@@ -4935,7 +4967,7 @@ function drawEngineSplashScreen(t)
|
|
|
4935
4967
|
x.font = '900 16px arial';
|
|
4936
4968
|
x.textAlign = 'center';
|
|
4937
4969
|
x.textBaseline = 'top';
|
|
4938
|
-
x.lineWidth = 1+p*3;
|
|
4970
|
+
x.lineWidth = .1+p*3.9;
|
|
4939
4971
|
let w2 = 0;
|
|
4940
4972
|
for (let i=0; i<s.length; ++i)
|
|
4941
4973
|
w2 += x.measureText(s[i]).width;
|