littlejsengine 1.8.9 → 1.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +17 -17
- package/build/littlejs.d.ts +352 -288
- package/build/littlejs.esm.js +695 -658
- package/build/littlejs.esm.min.js +1 -1
- package/build/littlejs.js +694 -658
- package/build/littlejs.min.js +1 -1
- package/build/littlejs.release.js +629 -594
- package/examples/breakout/game.js +4 -4
- package/examples/breakout/index.html +3 -3
- package/examples/breakoutTutorial/index.html +2 -2
- package/examples/electron/game.js +1 -1
- package/examples/electron/index.html +2 -2
- package/examples/favicon.png +0 -0
- package/examples/js13k/build.js +1 -1
- package/examples/js13k/index.html +13 -13
- package/examples/module/game.js +1 -1
- package/examples/module/index.html +1 -1
- package/examples/particles/index.html +1 -1
- package/examples/platformer/game.js +6 -6
- package/examples/platformer/gameCharacter.js +293 -0
- package/examples/platformer/gameEffects.js +8 -5
- package/examples/platformer/gameObjects.js +13 -13
- package/examples/platformer/gamePlayer.js +9 -293
- package/examples/platformer/index.html +7 -6
- package/examples/puzzle/game.js +3 -2
- package/examples/puzzle/index.html +2 -2
- package/examples/starter/build.js +1 -1
- package/examples/starter/game.js +2 -2
- package/examples/starter/index.html +13 -13
- package/examples/stress/index.html +35 -27
- package/examples/typescript/index.html +1 -1
- package/index.d.ts +2094 -0
- package/package.json +1 -1
- package/src/engine.js +28 -28
- package/src/engineAudio.js +57 -57
- package/src/engineDebug.js +66 -65
- package/src/engineDraw.js +64 -73
- package/src/engineExport.js +1 -0
- package/src/engineInput.js +57 -40
- package/src/engineMedals.js +32 -29
- package/src/engineObject.js +42 -27
- package/src/engineParticles.js +98 -72
- package/src/engineRelease.js +1 -1
- package/src/engineSettings.js +22 -22
- package/src/engineTileLayer.js +66 -60
- package/src/engineUtilities.js +49 -49
- package/src/engineWebGL.js +112 -135
- package/src/jsconfig.json +10 -0
|
@@ -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
|
|
@@ -156,11 +156,11 @@ function smoothStep(percent) { return percent * percent * (3 - 2 * percent); }
|
|
|
156
156
|
function nearestPowerOfTwo(value) { return 2**Math.ceil(Math.log2(value)); }
|
|
157
157
|
|
|
158
158
|
/** Returns true if two axis aligned bounding boxes are overlapping
|
|
159
|
-
* @param {Vector2} pointA
|
|
160
|
-
* @param {Vector2} sizeA
|
|
161
|
-
* @param {Vector2} pointB
|
|
162
|
-
* @param {Vector2} sizeB
|
|
163
|
-
* @return {Boolean}
|
|
159
|
+
* @param {Vector2} pointA - Center of box A
|
|
160
|
+
* @param {Vector2} sizeA - Size of box A
|
|
161
|
+
* @param {Vector2} pointB - Center of box B
|
|
162
|
+
* @param {Vector2} sizeB - Size of box B
|
|
163
|
+
* @return {Boolean} - True if overlapping
|
|
164
164
|
* @memberof Utilities */
|
|
165
165
|
function isOverlapping(pointA, sizeA, pointB, sizeB)
|
|
166
166
|
{
|
|
@@ -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=(0,0)] - World space position of the object
|
|
1183
|
+
* @param {Vector2} [size=(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=(1,1,1,1)] - 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} */
|
|
@@ -1469,8 +1484,8 @@ class EngineObject
|
|
|
1469
1484
|
|
|
1470
1485
|
/** Attaches a child to this with a given local transform
|
|
1471
1486
|
* @param {EngineObject} child
|
|
1472
|
-
* @param {Vector2} [localPos=
|
|
1473
|
-
* @param {Number} [localAngle
|
|
1487
|
+
* @param {Vector2} [localPos=(0,0)]
|
|
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=
|
|
1590
|
-
* @param {(Number|Vector2)} [size=tileSizeDefault]
|
|
1591
|
-
* @param {Number} [textureIndex
|
|
1604
|
+
* @param {(Number|Vector2)} [pos=(0,0)] - 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)
|
|
@@ -1629,9 +1644,9 @@ function tile(pos=vec2(), size=tileSizeDefault, textureIndex=0)
|
|
|
1629
1644
|
class TileInfo
|
|
1630
1645
|
{
|
|
1631
1646
|
/** Create a tile info object
|
|
1632
|
-
* @param {Vector2} [pos=
|
|
1647
|
+
* @param {Vector2} [pos=(0,0)] - 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);
|
|
@@ -1703,26 +1721,24 @@ function worldToScreen(worldPos)
|
|
|
1703
1721
|
}
|
|
1704
1722
|
|
|
1705
1723
|
/** Draw textured tile centered in world space, with color applied if using WebGL
|
|
1706
|
-
* @param {Vector2} pos
|
|
1707
|
-
* @param {Vector2} [size=
|
|
1708
|
-
* @param {TileInfo}[tileInfo]
|
|
1709
|
-
* @param {
|
|
1710
|
-
* @param {
|
|
1711
|
-
* @param {
|
|
1712
|
-
* @param {
|
|
1713
|
-
* @param {
|
|
1714
|
-
* @param {Boolean} [
|
|
1715
|
-
* @param {
|
|
1716
|
-
* @param {CanvasRenderingContext2D} [context] - Canvas 2D context to draw to
|
|
1724
|
+
* @param {Vector2} pos - Center of the tile in world space
|
|
1725
|
+
* @param {Vector2} [size=(1,1)] - Size of the tile in world space
|
|
1726
|
+
* @param {TileInfo}[tileInfo] - Tile info to use, untextured if undefined
|
|
1727
|
+
* @param {Color} [color=(1,1,1,1)] - Color to modulate with
|
|
1728
|
+
* @param {Number} [angle] - Angle to rotate by
|
|
1729
|
+
* @param {Boolean} [mirror] - If true image is flipped along the Y axis
|
|
1730
|
+
* @param {Color} [additiveColor=(0,0,0,0)] - Additive color to be applied
|
|
1731
|
+
* @param {Boolean} [useWebGL=glEnable] - Use accelerated WebGL rendering
|
|
1732
|
+
* @param {Boolean} [screenSpace=false] - If true the pos and size are in screen space
|
|
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)
|
|
@@ -1781,51 +1798,40 @@ function drawTile(pos, size=vec2(1), tileInfo, color=new Color,
|
|
|
1781
1798
|
|
|
1782
1799
|
/** Draw colored rect centered on pos
|
|
1783
1800
|
* @param {Vector2} pos
|
|
1784
|
-
* @param {Vector2} [size=
|
|
1785
|
-
* @param {Color} [color=
|
|
1786
|
-
* @param {Number} [angle
|
|
1801
|
+
* @param {Vector2} [size=(1,1)]
|
|
1802
|
+
* @param {Color} [color=(1,1,1,1)]
|
|
1803
|
+
* @param {Number} [angle]
|
|
1787
1804
|
* @param {Boolean} [useWebGL=glEnable]
|
|
1788
|
-
* @param {Boolean} [screenSpace=
|
|
1805
|
+
* @param {Boolean} [screenSpace=false]
|
|
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
|
-
* @param {Color} [color=
|
|
1799
|
-
* @param {Boolean} [
|
|
1800
|
-
* @param {
|
|
1801
|
-
* @param {CanvasRenderingContext2D} [context]
|
|
1815
|
+
* @param {Color} [color=(1,1,1,1)]
|
|
1816
|
+
* @param {Boolean} [screenSpace=false]
|
|
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
|
|
1826
|
-
* @param {Color} [color=
|
|
1831
|
+
* @param {Number} [thickness]
|
|
1832
|
+
* @param {Color} [color=(1,1,1,1)]
|
|
1827
1833
|
* @param {Boolean} [useWebGL=glEnable]
|
|
1828
|
-
* @param {Boolean} [screenSpace=
|
|
1834
|
+
* @param {Boolean} [screenSpace=false]
|
|
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=false]
|
|
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
|
|
1886
|
-
* @param {Color} [color=
|
|
1887
|
-
* @param {Number} [lineWidth
|
|
1888
|
-
* @param {Color} [lineColor=
|
|
1889
|
-
* @param {
|
|
1891
|
+
* @param {Number} [size]
|
|
1892
|
+
* @param {Color} [color=(1,1,1,1)]
|
|
1893
|
+
* @param {Number} [lineWidth]
|
|
1894
|
+
* @param {Color} [lineColor=(0,0,0,1)]
|
|
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
|
|
1903
|
-
* @param {Color} [color=
|
|
1904
|
-
* @param {Number} [lineWidth
|
|
1905
|
-
* @param {Color} [lineColor=
|
|
1906
|
-
* @param {
|
|
1908
|
+
* @param {Number} [size]
|
|
1909
|
+
* @param {Color} [color=(1,1,1,1)]
|
|
1910
|
+
* @param {Number} [lineWidth]
|
|
1911
|
+
* @param {Color} [lineColor=(0,0,0,1)]
|
|
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';
|
|
@@ -1945,9 +1951,9 @@ let engineFontImage;
|
|
|
1945
1951
|
class FontImage
|
|
1946
1952
|
{
|
|
1947
1953
|
/** Create an image font
|
|
1948
|
-
* @param {HTMLImageElement} [image]
|
|
1949
|
-
* @param {Vector2} [tileSize=
|
|
1950
|
-
* @param {Vector2} [paddingSize=
|
|
1954
|
+
* @param {HTMLImageElement} [image] - Image for the font, if undefined default font is used
|
|
1955
|
+
* @param {Vector2} [tileSize=(8,8)] - Size of the font source tiles
|
|
1956
|
+
* @param {Vector2} [paddingSize=(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; mousePosScreen = mouseToScreen(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)=> mouseWheel = e.ctrlKey ? 0 : 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|Array} [pattern] - single value in ms 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
|
{
|
|
@@ -3112,7 +3135,7 @@ function getTileCollisionData(pos)
|
|
|
3112
3135
|
|
|
3113
3136
|
/** Check if collision with another object should occur
|
|
3114
3137
|
* @param {Vector2} pos
|
|
3115
|
-
* @param {Vector2} [size=
|
|
3138
|
+
* @param {Vector2} [size=(1,1)]
|
|
3116
3139
|
* @param {EngineObject} [object]
|
|
3117
3140
|
* @return {Boolean}
|
|
3118
3141
|
* @memberof TileCollision */
|
|
@@ -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=(0,0)] - World space position
|
|
3255
|
+
* @param {Vector2} [size=tileCollisionSize] - World space size
|
|
3256
|
+
* @param {TileInfo} [tileInfo] - Tile info for layer
|
|
3257
|
+
* @param {Vector2} [scale=(1,1)] - How much to scale this layer when rendered
|
|
3258
|
+
* @param {Number} [renderOrder] - Objects are sorted by renderOrder
|
|
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);
|
|
@@ -3293,33 +3316,38 @@ constructor(pos, size=tileCollisionSize, tileInfo=tile(), scale=vec2(1), renderO
|
|
|
3293
3316
|
}
|
|
3294
3317
|
|
|
3295
3318
|
/** Draw all the tile data to an offscreen canvas
|
|
3296
|
-
* - This may be slow in some browsers
|
|
3297
|
-
*/
|
|
3319
|
+
* - This may be slow in some browsers but only needs to be done once */
|
|
3298
3320
|
redraw()
|
|
3299
3321
|
{
|
|
3300
|
-
this.redrawStart(
|
|
3301
|
-
this.
|
|
3322
|
+
this.redrawStart(true);
|
|
3323
|
+
for (let x = this.size.x; x--;)
|
|
3324
|
+
for (let y = this.size.y; y--;)
|
|
3325
|
+
this.drawTileData(vec2(x,y), false);
|
|
3302
3326
|
this.redrawEnd();
|
|
3303
3327
|
}
|
|
3304
3328
|
|
|
3305
3329
|
/** Call to start the redraw process
|
|
3306
|
-
*
|
|
3307
|
-
|
|
3330
|
+
* - This can be used to manually update small parts of the level
|
|
3331
|
+
* @param {Boolean} [clear] - Should it clear the canvas before drawing */
|
|
3332
|
+
redrawStart(clear=false)
|
|
3308
3333
|
{
|
|
3309
3334
|
// save current render settings
|
|
3335
|
+
/** @type {[HTMLCanvasElement, CanvasRenderingContext2D, Vector2, Vector2, number]} */
|
|
3310
3336
|
this.savedRenderSettings = [mainCanvas, mainContext, mainCanvasSize, cameraPos, cameraScale];
|
|
3311
3337
|
|
|
3312
|
-
//
|
|
3338
|
+
// use webgl rendering system to render the tiles if enabled
|
|
3339
|
+
// this works by temporally taking control of the rendering system
|
|
3313
3340
|
mainCanvas = this.canvas;
|
|
3314
3341
|
mainContext = this.context;
|
|
3342
|
+
mainCanvasSize = this.size.multiply(this.tileInfo.size);
|
|
3315
3343
|
cameraPos = this.size.scale(.5);
|
|
3316
3344
|
cameraScale = this.tileInfo.size.x;
|
|
3317
3345
|
|
|
3318
3346
|
if (clear)
|
|
3319
3347
|
{
|
|
3320
3348
|
// clear and set size
|
|
3321
|
-
mainCanvas.width =
|
|
3322
|
-
mainCanvas.height =
|
|
3349
|
+
mainCanvas.width = mainCanvasSize.x;
|
|
3350
|
+
mainCanvas.height = mainCanvasSize.y;
|
|
3323
3351
|
}
|
|
3324
3352
|
|
|
3325
3353
|
// begin a new render for the tile canvas
|
|
@@ -3329,40 +3357,41 @@ constructor(pos, size=tileCollisionSize, tileInfo=tile(), scale=vec2(1), renderO
|
|
|
3329
3357
|
/** Call to end the redraw process */
|
|
3330
3358
|
redrawEnd()
|
|
3331
3359
|
{
|
|
3332
|
-
ASSERT(mainContext == this.context
|
|
3333
|
-
glEnable && glCopyToContext(mainContext,
|
|
3360
|
+
ASSERT(mainContext == this.context, 'must call redrawStart() before drawing tiles');
|
|
3361
|
+
glEnable && glCopyToContext(mainContext, true);
|
|
3334
3362
|
//debugSaveCanvas(this.canvas);
|
|
3335
3363
|
|
|
3336
3364
|
// set stuff back to normal
|
|
3337
3365
|
[mainCanvas, mainContext, mainCanvasSize, cameraPos, cameraScale] = this.savedRenderSettings;
|
|
3338
3366
|
}
|
|
3339
3367
|
|
|
3340
|
-
/** Draw the tile at a given position
|
|
3341
|
-
*
|
|
3342
|
-
|
|
3368
|
+
/** Draw the tile at a given position in the tile grid
|
|
3369
|
+
* This can be used to clear out tiles when they are destroyed
|
|
3370
|
+
* Tiles can also be redrawn if isinde a redrawStart/End block
|
|
3371
|
+
* @param {Vector2} layerPos
|
|
3372
|
+
* @param {Boolean} [clear] - should the old tile be cleared out
|
|
3373
|
+
*/
|
|
3374
|
+
drawTileData(layerPos, clear=true)
|
|
3343
3375
|
{
|
|
3344
|
-
//
|
|
3345
|
-
const
|
|
3346
|
-
|
|
3376
|
+
// clear out where the tile was, for full opaque tiles this can be skipped
|
|
3377
|
+
const s = this.tileInfo.size;
|
|
3378
|
+
if (clear)
|
|
3379
|
+
{
|
|
3380
|
+
const pos = layerPos.multiply(s);
|
|
3381
|
+
this.context.clearRect(pos.x, this.canvas.height-pos.y, s.x, -s.y);
|
|
3382
|
+
}
|
|
3347
3383
|
|
|
3348
3384
|
// draw the tile if not undefined
|
|
3349
3385
|
const d = this.getData(layerPos);
|
|
3350
3386
|
if (d.tile != undefined)
|
|
3351
3387
|
{
|
|
3352
|
-
|
|
3353
|
-
|
|
3388
|
+
const pos = this.pos.add(layerPos).add(vec2(.5));
|
|
3389
|
+
ASSERT(mainContext == this.context, 'must call redrawStart() before drawing tiles');
|
|
3390
|
+
const tileInfo = tile(d.tile, s, this.tileInfo.textureIndex);
|
|
3354
3391
|
drawTile(pos, vec2(1), tileInfo, d.color, d.direction*PI/2, d.mirror);
|
|
3355
3392
|
}
|
|
3356
3393
|
}
|
|
3357
3394
|
|
|
3358
|
-
/** Draw all the tiles in this layer */
|
|
3359
|
-
drawAllTileData()
|
|
3360
|
-
{
|
|
3361
|
-
for (let x = this.size.x; x--;)
|
|
3362
|
-
for (let y = this.size.y; y--;)
|
|
3363
|
-
this.drawTileData(vec2(x,y));
|
|
3364
|
-
}
|
|
3365
|
-
|
|
3366
3395
|
/** Draw directly to the 2D canvas in world space (bipass webgl)
|
|
3367
3396
|
* @param {Vector2} pos
|
|
3368
3397
|
* @param {Vector2} size
|
|
@@ -3382,11 +3411,11 @@ constructor(pos, size=tileCollisionSize, tileInfo=tile(), scale=vec2(1), renderO
|
|
|
3382
3411
|
context.restore();
|
|
3383
3412
|
}
|
|
3384
3413
|
|
|
3385
|
-
/** Draw a tile directly onto the layer canvas
|
|
3414
|
+
/** Draw a tile directly onto the layer canvas in world space
|
|
3386
3415
|
* @param {Vector2} pos
|
|
3387
|
-
* @param {Vector2} [size=
|
|
3416
|
+
* @param {Vector2} [size=(1,1)]
|
|
3388
3417
|
* @param {TileInfo} [tileInfo]
|
|
3389
|
-
* @param {Color} [color=
|
|
3418
|
+
* @param {Color} [color=(1,1,1,1)]
|
|
3390
3419
|
* @param {Number} [angle=0]
|
|
3391
3420
|
* @param {Boolean} [mirror=0] */
|
|
3392
3421
|
drawTile(pos, size=vec2(1), tileInfo, color=new Color, angle, mirror)
|
|
@@ -3411,13 +3440,13 @@ constructor(pos, size=tileCollisionSize, tileInfo=tile(), scale=vec2(1), renderO
|
|
|
3411
3440
|
});
|
|
3412
3441
|
}
|
|
3413
3442
|
|
|
3414
|
-
/** Draw a rectangle directly onto the layer canvas
|
|
3443
|
+
/** Draw a rectangle directly onto the layer canvas in world space
|
|
3415
3444
|
* @param {Vector2} pos
|
|
3416
|
-
* @param {Vector2} [size=
|
|
3417
|
-
* @param {Color} [color=
|
|
3445
|
+
* @param {Vector2} [size=(1,1)]
|
|
3446
|
+
* @param {Color} [color=(1,1,1,1)]
|
|
3418
3447
|
* @param {Number} [angle=0] */
|
|
3419
3448
|
drawRect(pos, size, color, angle)
|
|
3420
|
-
{ this.drawTile(pos, size,
|
|
3449
|
+
{ this.drawTile(pos, size, undefined, color, angle); }
|
|
3421
3450
|
}
|
|
3422
3451
|
/**
|
|
3423
3452
|
* LittleJS Particle System
|
|
@@ -3446,36 +3475,36 @@ class ParticleEmitter extends EngineObject
|
|
|
3446
3475
|
{
|
|
3447
3476
|
/** Create a particle system with the given settings
|
|
3448
3477
|
* @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}
|
|
3478
|
+
* @param {Number} [angle] - Angle to emit the particles
|
|
3479
|
+
* @param {Number|Vector2} [emitSize] - World space size of the emitter (float for circle diameter, vec2 for rect)
|
|
3480
|
+
* @param {Number} [emitTime] - How long to stay alive (0 is forever)
|
|
3481
|
+
* @param {Number} [emitRate] - How many particles per second to spawn, does not emit if 0
|
|
3482
|
+
* @param {Number} [emitConeAngle=PI] - Local angle to apply velocity to particles from emitter
|
|
3454
3483
|
* @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
|
|
3484
|
+
* @param {Color} [colorStartA=(1,1,1,1)] - Color at start of life 1, randomized between start colors
|
|
3485
|
+
* @param {Color} [colorStartB=(1,1,1,1)] - Color at start of life 2, randomized between start colors
|
|
3486
|
+
* @param {Color} [colorEndA=(1,1,1,0)] - Color at end of life 1, randomized between end colors
|
|
3487
|
+
* @param {Color} [colorEndB=(1,1,1,0)] - Color at end of life 2, randomized between end colors
|
|
3488
|
+
* @param {Number} [particleTime] - How long particles live
|
|
3489
|
+
* @param {Number} [sizeStart] - How big are particles at start
|
|
3490
|
+
* @param {Number} [sizeEnd] - How big are particles at end
|
|
3491
|
+
* @param {Number} [speed] - How fast are particles when spawned
|
|
3492
|
+
* @param {Number} [angleSpeed] - How fast are particles rotating
|
|
3493
|
+
* @param {Number} [damping] - How much to dampen particle speed
|
|
3494
|
+
* @param {Number} [angleDamping] - How much to dampen particle angular speed
|
|
3495
|
+
* @param {Number} [gravityScale] - How much gravity effect particles
|
|
3496
|
+
* @param {Number} [particleConeAngle] - Cone for start particle angle
|
|
3497
|
+
* @param {Number} [fadeRate] - How quick to fade particles at start/end in percent of life
|
|
3498
|
+
* @param {Number} [randomness] - Apply extra randomness percent
|
|
3499
|
+
* @param {Boolean} [collideTiles] - Do particles collide against tiles
|
|
3500
|
+
* @param {Boolean} [additive] - Should particles use addtive blend
|
|
3501
|
+
* @param {Boolean} [randomColorLinear] - Should color be randomized linearly or across each component
|
|
3502
|
+
* @param {Number} [renderOrder] - Render order for particles (additive is above other stuff by default)
|
|
3503
|
+
* @param {Boolean} [localSpace] - Should it be in local space of emitter (world space is default)
|
|
3475
3504
|
*/
|
|
3476
3505
|
constructor
|
|
3477
3506
|
(
|
|
3478
|
-
|
|
3507
|
+
position,
|
|
3479
3508
|
angle,
|
|
3480
3509
|
emitSize = 0,
|
|
3481
3510
|
emitTime = 0,
|
|
@@ -3497,14 +3526,14 @@ class ParticleEmitter extends EngineObject
|
|
|
3497
3526
|
particleConeAngle = PI,
|
|
3498
3527
|
fadeRate = .1,
|
|
3499
3528
|
randomness = .2,
|
|
3500
|
-
collideTiles,
|
|
3501
|
-
additive,
|
|
3502
|
-
randomColorLinear =
|
|
3529
|
+
collideTiles = false,
|
|
3530
|
+
additive = false,
|
|
3531
|
+
randomColorLinear = true,
|
|
3503
3532
|
renderOrder = additive ? 1e9 : 0,
|
|
3504
|
-
localSpace
|
|
3533
|
+
localSpace = false
|
|
3505
3534
|
)
|
|
3506
3535
|
{
|
|
3507
|
-
super(
|
|
3536
|
+
super(position, vec2(), tileInfo, angle, undefined, renderOrder);
|
|
3508
3537
|
|
|
3509
3538
|
// emitter settings
|
|
3510
3539
|
/** @property {Number|Vector2} - World space size of the emitter (float for circle diameter, vec2 for rect) */
|
|
@@ -3553,14 +3582,17 @@ class ParticleEmitter extends EngineObject
|
|
|
3553
3582
|
this.randomness = randomness;
|
|
3554
3583
|
/** @property {Boolean} - Do particles collide against tiles */
|
|
3555
3584
|
this.collideTiles = collideTiles;
|
|
3556
|
-
/** @property {
|
|
3585
|
+
/** @property {Boolean} - Should particles use addtive blend */
|
|
3557
3586
|
this.additive = additive;
|
|
3558
3587
|
/** @property {Boolean} - Should it be in local space of emitter */
|
|
3559
|
-
this.localSpace
|
|
3560
|
-
/** @property {Number} - If
|
|
3588
|
+
this.localSpace = localSpace;
|
|
3589
|
+
/** @property {Number} - If non zero the partile is drawn as a trail, stretched in the drection of velocity */
|
|
3561
3590
|
this.trailScale = 0;
|
|
3562
|
-
|
|
3563
|
-
|
|
3591
|
+
/** @property {Function} - Callback when particle is destroyed */
|
|
3592
|
+
this.particleDestroyCallback = undefined;
|
|
3593
|
+
/** @property {Function} - Callback when particle is created */
|
|
3594
|
+
this.particleCreateCallback = undefined;
|
|
3595
|
+
/** @property {Number} - Track particle emit time */
|
|
3564
3596
|
this.emitTimeBuffer = 0;
|
|
3565
3597
|
}
|
|
3566
3598
|
|
|
@@ -3592,18 +3624,16 @@ class ParticleEmitter extends EngineObject
|
|
|
3592
3624
|
emitParticle()
|
|
3593
3625
|
{
|
|
3594
3626
|
// spawn a particle
|
|
3595
|
-
let pos = this.emitSize
|
|
3596
|
-
|
|
3597
|
-
|
|
3598
|
-
|
|
3627
|
+
let pos = typeof this.emitSize === 'number' ? // check if number was used
|
|
3628
|
+
randInCircle(this.emitSize/2) // circle emitter
|
|
3629
|
+
: vec2(rand(-.5,.5), rand(-.5,.5)) // box emitter
|
|
3630
|
+
.multiply(this.emitSize).rotate(this.angle)
|
|
3599
3631
|
let angle = rand(this.particleConeAngle, -this.particleConeAngle);
|
|
3600
3632
|
if (!this.localSpace)
|
|
3601
3633
|
{
|
|
3602
3634
|
pos = this.pos.add(pos);
|
|
3603
3635
|
angle += this.angle;
|
|
3604
3636
|
}
|
|
3605
|
-
|
|
3606
|
-
const particle = new Particle(pos, this.tileInfo, this.tileSize, angle);
|
|
3607
3637
|
|
|
3608
3638
|
// randomness scales each paremeter by a percentage
|
|
3609
3639
|
const randomness = this.randomness;
|
|
@@ -3619,30 +3649,21 @@ class ParticleEmitter extends EngineObject
|
|
|
3619
3649
|
const colorStart = randColor(this.colorStartA, this.colorStartB, this.randomColorLinear);
|
|
3620
3650
|
const colorEnd = randColor(this.colorEndA, this.colorEndB, this.randomColorLinear);
|
|
3621
3651
|
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;
|
|
3652
|
+
|
|
3653
|
+
// build particle
|
|
3654
|
+
const particle = new Particle(pos, this.tileInfo, angle, colorStart, colorEnd, particleTime, sizeStart, sizeEnd, this.fadeRate, this.additive, this.trailScale, this.localSpace && this, this.particleDestroyCallback);
|
|
3655
|
+
particle.velocity = vec2().setAngle(velocityAngle, speed);
|
|
3656
|
+
particle.fadeRate = this.fadeRate;
|
|
3657
|
+
particle.damping = this.damping;
|
|
3658
|
+
particle.angleDamping = this.angleDamping;
|
|
3659
|
+
particle.elasticity = this.elasticity;
|
|
3660
|
+
particle.friction = this.friction;
|
|
3661
|
+
particle.gravityScale = this.gravityScale;
|
|
3662
|
+
particle.collideTiles = this.collideTiles;
|
|
3663
|
+
particle.renderOrder = this.renderOrder;
|
|
3664
|
+
particle.mirror = !!randInt(2);
|
|
3665
|
+
|
|
3666
|
+
// call particle create callaback
|
|
3646
3667
|
this.particleCreateCallback && this.particleCreateCallback(particle);
|
|
3647
3668
|
|
|
3648
3669
|
// return the newly created particle
|
|
@@ -3661,13 +3682,47 @@ class ParticleEmitter extends EngineObject
|
|
|
3661
3682
|
class Particle extends EngineObject
|
|
3662
3683
|
{
|
|
3663
3684
|
/**
|
|
3664
|
-
* Create a particle with the given
|
|
3665
|
-
* @param {Vector2}
|
|
3666
|
-
* @param {TileInfo} [tileInfo]
|
|
3667
|
-
* @param {Number}
|
|
3685
|
+
* Create a particle with the given shis.colorStart = undefined;ettings
|
|
3686
|
+
* @param {Vector2} position - World space position of the particle
|
|
3687
|
+
* @param {TileInfo} [tileInfo] - Tile info to render particles
|
|
3688
|
+
* @param {Number} [angle] - Angle to rotate the particle
|
|
3689
|
+
* @param {Color} [colorStart] - Color at start of life
|
|
3690
|
+
* @param {Color} [colorEnd] - Color at end of life
|
|
3691
|
+
* @param {Number} [lifeTime] - How long to live for
|
|
3692
|
+
* @param {Number} [sizeStart] - Angle to rotate the particle
|
|
3693
|
+
* @param {Number} [sizeEnd] - Angle to rotate the particle
|
|
3694
|
+
* @param {Number} [fadeRate] - Angle to rotate the particle
|
|
3695
|
+
* @param {Boolean} [additive] - Angle to rotate the particle
|
|
3696
|
+
* @param {Number} [trailScale] - If a trail, how long to make it
|
|
3697
|
+
* @param {ParticleEmitter} [localSpaceEmitter] - Parent emitter if local space
|
|
3698
|
+
* @param {Function} [destroyCallback] - Called when particle dies
|
|
3668
3699
|
*/
|
|
3669
|
-
constructor(
|
|
3670
|
-
|
|
3700
|
+
constructor(position, tileInfo, angle, colorStart, colorEnd, lifeTime, sizeStart, sizeEnd, fadeRate, additive, trailScale, localSpaceEmitter, destroyCallback
|
|
3701
|
+
)
|
|
3702
|
+
{
|
|
3703
|
+
super(position, vec2(), tileInfo, angle);
|
|
3704
|
+
|
|
3705
|
+
/** @property {Color} - Color at start of life */
|
|
3706
|
+
this.colorStart = colorStart;
|
|
3707
|
+
/** @property {Color} - Calculated change in color */
|
|
3708
|
+
this.colorEndDelta = colorEnd.subtract(colorStart);
|
|
3709
|
+
/** @property {Number} - How long to live for */
|
|
3710
|
+
this.lifeTime = lifeTime;
|
|
3711
|
+
/** @property {Number} - Size at start of life */
|
|
3712
|
+
this.sizeStart = sizeStart;
|
|
3713
|
+
/** @property {Number} - Calculated change in size */
|
|
3714
|
+
this.sizeEndDelta = sizeEnd - sizeStart;
|
|
3715
|
+
/** @property {Number} - How quick to fade in/out */
|
|
3716
|
+
this.fadeRate = fadeRate;
|
|
3717
|
+
/** @property {Boolean} - Is it additive */
|
|
3718
|
+
this.additive = additive;
|
|
3719
|
+
/** @property {Number} - If a trail, how long to make it */
|
|
3720
|
+
this.trailScale = trailScale;
|
|
3721
|
+
/** @property {ParticleEmitter} - Parent emitter if local space */
|
|
3722
|
+
this.localSpaceEmitter = localSpaceEmitter;
|
|
3723
|
+
/** @property {Function} - Called when particle dies */
|
|
3724
|
+
this.destroyCallback = destroyCallback;
|
|
3725
|
+
}
|
|
3671
3726
|
|
|
3672
3727
|
/** Render the particle, automatically called each frame, sorted by renderOrder */
|
|
3673
3728
|
render()
|
|
@@ -3685,7 +3740,7 @@ class Particle extends EngineObject
|
|
|
3685
3740
|
(p < fadeRate ? p/fadeRate : p > 1-fadeRate ? (1-p)/fadeRate : 1)); // fade alpha
|
|
3686
3741
|
|
|
3687
3742
|
// draw the particle
|
|
3688
|
-
this.additive && setBlendMode(
|
|
3743
|
+
this.additive && setBlendMode(true);
|
|
3689
3744
|
|
|
3690
3745
|
let pos = this.pos, angle = this.angle;
|
|
3691
3746
|
if (this.localSpaceEmitter)
|
|
@@ -3775,7 +3830,7 @@ class Medal
|
|
|
3775
3830
|
* @param {Number} id - The unique identifier of the medal
|
|
3776
3831
|
* @param {String} name - Name of the medal
|
|
3777
3832
|
* @param {String} [description] - Description of the medal
|
|
3778
|
-
* @param {String} [icon
|
|
3833
|
+
* @param {String} [icon] - Icon for the medal
|
|
3779
3834
|
* @param {String} [src] - Image location for the medal
|
|
3780
3835
|
*/
|
|
3781
3836
|
constructor(id, name, description='', icon='🏆', src)
|
|
@@ -3798,14 +3853,14 @@ class Medal
|
|
|
3798
3853
|
return;
|
|
3799
3854
|
|
|
3800
3855
|
// save the medal
|
|
3801
|
-
ASSERT(medalsSaveName
|
|
3856
|
+
ASSERT(medalsSaveName, 'save name must be set');
|
|
3802
3857
|
localStorage[this.storageKey()] = this.unlocked = 1;
|
|
3803
3858
|
medalsDisplayQueue.push(this);
|
|
3804
3859
|
newgrounds && newgrounds.unlockMedal(this.id);
|
|
3805
3860
|
}
|
|
3806
3861
|
|
|
3807
3862
|
/** Render a medal
|
|
3808
|
-
* @param {Number} [hidePercent
|
|
3863
|
+
* @param {Number} [hidePercent] - How much to slide the medal off screen
|
|
3809
3864
|
*/
|
|
3810
3865
|
render(hidePercent=0)
|
|
3811
3866
|
{
|
|
@@ -3817,25 +3872,25 @@ class Medal
|
|
|
3817
3872
|
// draw containing rect and clip to that region
|
|
3818
3873
|
context.save();
|
|
3819
3874
|
context.beginPath();
|
|
3820
|
-
context.fillStyle =
|
|
3821
|
-
context.strokeStyle =
|
|
3875
|
+
context.fillStyle = rgb(.9,.9,.9).toString();
|
|
3876
|
+
context.strokeStyle = rgb(0,0,0).toString();
|
|
3822
3877
|
context.lineWidth = 3;
|
|
3823
|
-
context.
|
|
3878
|
+
context.rect(x, y, width, medalDisplaySize.y);
|
|
3879
|
+
context.fill();
|
|
3824
3880
|
context.stroke();
|
|
3825
3881
|
context.clip();
|
|
3826
3882
|
|
|
3827
3883
|
// draw the icon and text
|
|
3828
3884
|
this.renderIcon(vec2(x+15+medalDisplayIconSize/2, y+medalDisplaySize.y/2));
|
|
3829
3885
|
const pos = vec2(x+medalDisplayIconSize+30, y+28);
|
|
3830
|
-
drawTextScreen(this.name, pos, 38, new Color(0,0,0), 0,
|
|
3886
|
+
drawTextScreen(this.name, pos, 38, new Color(0,0,0), 0, undefined, 'left');
|
|
3831
3887
|
pos.y += 32;
|
|
3832
|
-
drawTextScreen(this.description, pos, 24, new Color(0,0,0), 0,
|
|
3888
|
+
drawTextScreen(this.description, pos, 24, new Color(0,0,0), 0, undefined, 'left');
|
|
3833
3889
|
context.restore();
|
|
3834
3890
|
}
|
|
3835
3891
|
|
|
3836
3892
|
/** Render the icon for a medal
|
|
3837
|
-
* @param {
|
|
3838
|
-
* @param {Number} y - Screen space Y position
|
|
3893
|
+
* @param {Vector2} pos - Screen space position
|
|
3839
3894
|
* @param {Number} [size=medalDisplayIconSize] - Screen space size
|
|
3840
3895
|
*/
|
|
3841
3896
|
renderIcon(pos, size=medalDisplayIconSize)
|
|
@@ -3863,7 +3918,10 @@ function medalsRender()
|
|
|
3863
3918
|
if (!medalsDisplayTimeLast)
|
|
3864
3919
|
medalsDisplayTimeLast = timeReal;
|
|
3865
3920
|
else if (time > medalDisplayTime)
|
|
3866
|
-
|
|
3921
|
+
{
|
|
3922
|
+
medalsDisplayTimeLast = 0;
|
|
3923
|
+
medalsDisplayQueue.shift();
|
|
3924
|
+
}
|
|
3867
3925
|
else
|
|
3868
3926
|
{
|
|
3869
3927
|
// slide on/off medals
|
|
@@ -3903,8 +3961,8 @@ class Newgrounds
|
|
|
3903
3961
|
* @param {Object} [cryptoJS] - An instance of CryptoJS, if there is a cipher */
|
|
3904
3962
|
constructor(app_id, cipher, cryptoJS)
|
|
3905
3963
|
{
|
|
3906
|
-
ASSERT(!newgrounds && app_id
|
|
3907
|
-
ASSERT(!cipher || cryptoJS
|
|
3964
|
+
ASSERT(!newgrounds && app_id>0, 'there can only be one newgrounds object');
|
|
3965
|
+
ASSERT(!cipher || cryptoJS, 'must provide cryptojs if there is a cipher');
|
|
3908
3966
|
|
|
3909
3967
|
this.app_id = app_id;
|
|
3910
3968
|
this.cipher = cipher;
|
|
@@ -3947,39 +4005,39 @@ class Newgrounds
|
|
|
3947
4005
|
debugMedals && console.log(this.scoreboards);
|
|
3948
4006
|
|
|
3949
4007
|
const keepAliveMS = 5 * 60 * 1e3;
|
|
3950
|
-
setInterval(()=>this.call('Gateway.ping', 0,
|
|
4008
|
+
setInterval(()=>this.call('Gateway.ping', 0, true), keepAliveMS);
|
|
3951
4009
|
}
|
|
3952
4010
|
|
|
3953
4011
|
/** Send message to unlock a medal by id
|
|
3954
4012
|
* @param {Number} id - The medal id */
|
|
3955
|
-
unlockMedal(id) { return this.call('Medal.unlock', {'id':id},
|
|
4013
|
+
unlockMedal(id) { return this.call('Medal.unlock', {'id':id}, true); }
|
|
3956
4014
|
|
|
3957
4015
|
/** Send message to post score
|
|
3958
4016
|
* @param {Number} id - The scoreboard id
|
|
3959
4017
|
* @param {Number} value - The score value */
|
|
3960
|
-
postScore(id, value) { return this.call('ScoreBoard.postScore', {'id':id, 'value':value},
|
|
4018
|
+
postScore(id, value) { return this.call('ScoreBoard.postScore', {'id':id, 'value':value}, true); }
|
|
3961
4019
|
|
|
3962
4020
|
/** 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}
|
|
4021
|
+
* @param {Number} id - The scoreboard id
|
|
4022
|
+
* @param {String} [user] - A user's id or name
|
|
4023
|
+
* @param {Number} [social] - If true, only social scores will be loaded
|
|
4024
|
+
* @param {Number} [skip] - Number of scores to skip before start
|
|
4025
|
+
* @param {Number} [limit] - Number of scores to include in the list
|
|
4026
|
+
* @return {Object} - The response JSON object
|
|
3969
4027
|
*/
|
|
3970
|
-
getScores(id, user
|
|
4028
|
+
getScores(id, user, social=0, skip=0, limit=10)
|
|
3971
4029
|
{ return this.call('ScoreBoard.getScores', {'id':id, 'user':user, 'social':social, 'skip':skip, 'limit':limit}); }
|
|
3972
4030
|
|
|
3973
4031
|
/** Send message to log a view */
|
|
3974
|
-
logView() { return this.call('App.logView', {'host':this.host},
|
|
4032
|
+
logView() { return this.call('App.logView', {'host':this.host}, true); }
|
|
3975
4033
|
|
|
3976
4034
|
/** 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}
|
|
4035
|
+
* @param {String} component - Name of the component
|
|
4036
|
+
* @param {Object} [parameters] - Parameters to use for call
|
|
4037
|
+
* @param {Boolean} [async] - If true, don't wait for response before continuing
|
|
4038
|
+
* @return {Object} - The response JSON object
|
|
3981
4039
|
*/
|
|
3982
|
-
call(component, parameters
|
|
4040
|
+
call(component, parameters, async=false)
|
|
3983
4041
|
{
|
|
3984
4042
|
const call = {'component':component, 'parameters':parameters};
|
|
3985
4043
|
if (this.cipher)
|
|
@@ -4014,7 +4072,7 @@ class Newgrounds
|
|
|
4014
4072
|
return xmlHttp.responseText && JSON.parse(xmlHttp.responseText);
|
|
4015
4073
|
}
|
|
4016
4074
|
}
|
|
4017
|
-
/**
|
|
4075
|
+
/**
|
|
4018
4076
|
* LittleJS WebGL Interface
|
|
4019
4077
|
* - All webgl used by the engine is wrapped up here
|
|
4020
4078
|
* - For normal stuff you won't need to see or call anything in this file
|
|
@@ -4033,13 +4091,13 @@ class Newgrounds
|
|
|
4033
4091
|
* @memberof WebGL */
|
|
4034
4092
|
let glCanvas;
|
|
4035
4093
|
|
|
4036
|
-
/** 2d context for glCanvas
|
|
4037
|
-
* @type {
|
|
4094
|
+
/** 2d context for glCanvas
|
|
4095
|
+
* @type {WebGL2RenderingContext}
|
|
4038
4096
|
* @memberof WebGL */
|
|
4039
4097
|
let glContext;
|
|
4040
4098
|
|
|
4041
4099
|
// WebGL internal variables not exposed to documentation
|
|
4042
|
-
let
|
|
4100
|
+
let glShader, glActiveTexture, glArrayBuffer, glGeometryBuffer, glPositionData, glColorData, glInstanceCount, glAdditive, glBatchAdditive;
|
|
4043
4101
|
|
|
4044
4102
|
///////////////////////////////////////////////////////////////////////////////
|
|
4045
4103
|
|
|
@@ -4055,73 +4113,89 @@ function glInit()
|
|
|
4055
4113
|
|
|
4056
4114
|
// setup vertex and fragment shaders
|
|
4057
4115
|
glShader = glCreateProgram(
|
|
4058
|
-
'#version 300 es\n' +
|
|
4059
|
-
'precision highp float;'+
|
|
4060
|
-
'uniform mat4 m;'+
|
|
4061
|
-
'in
|
|
4062
|
-
'
|
|
4063
|
-
'
|
|
4064
|
-
'
|
|
4065
|
-
'
|
|
4066
|
-
'
|
|
4116
|
+
'#version 300 es\n' + // specify GLSL ES version
|
|
4117
|
+
'precision highp float;'+ // use highp for better accuracy
|
|
4118
|
+
'uniform mat4 m;'+ // transform matrix
|
|
4119
|
+
'in vec2 g;'+ // geometry
|
|
4120
|
+
'in vec4 p,u,c,a;'+ // position/size, uvs, color, additiveColor
|
|
4121
|
+
'in float r;'+ // rotation
|
|
4122
|
+
'out vec2 v;'+ // return uv, color, additiveColor
|
|
4123
|
+
'out vec4 d,e;'+ // return uv, color, additiveColor
|
|
4124
|
+
'void main(){'+ // shader entry point
|
|
4125
|
+
'vec2 s=(g-.5)*p.zw;'+ // get size offset
|
|
4126
|
+
'gl_Position=m*vec4(p.xy+s*cos(r)-vec2(-s.y,s)*sin(r),1,1);'+ // transform position
|
|
4127
|
+
'v=mix(u.xw,u.zy,g);'+ // pass uv to fragment shader
|
|
4128
|
+
'd=c;e=a;'+ // pass colors to fragment shader
|
|
4129
|
+
'}' // end of shader
|
|
4067
4130
|
,
|
|
4068
|
-
'#version 300 es\n' +
|
|
4069
|
-
'precision highp float;'+
|
|
4070
|
-
'in
|
|
4071
|
-
'
|
|
4072
|
-
'
|
|
4073
|
-
'
|
|
4074
|
-
'
|
|
4075
|
-
'
|
|
4131
|
+
'#version 300 es\n' + // specify GLSL ES version
|
|
4132
|
+
'precision highp float;'+ // use highp for better accuracy
|
|
4133
|
+
'in vec2 v;'+ // uv
|
|
4134
|
+
'in vec4 d,e;'+ // color, additiveColor
|
|
4135
|
+
'uniform sampler2D s;'+ // texture
|
|
4136
|
+
'out vec4 c;'+ // out color
|
|
4137
|
+
'void main(){'+ // shader entry point
|
|
4138
|
+
'c=texture(s,v)*d+e;'+ // modulate texture by color plus additive
|
|
4139
|
+
'}' // end of shader
|
|
4076
4140
|
);
|
|
4077
4141
|
|
|
4078
4142
|
// init buffers
|
|
4079
|
-
|
|
4080
|
-
glPositionData = new Float32Array(
|
|
4081
|
-
glColorData = new Uint32Array(
|
|
4143
|
+
const glInstanceData = new ArrayBuffer(gl_INSTANCE_BUFFER_SIZE);
|
|
4144
|
+
glPositionData = new Float32Array(glInstanceData);
|
|
4145
|
+
glColorData = new Uint32Array(glInstanceData);
|
|
4082
4146
|
glArrayBuffer = glContext.createBuffer();
|
|
4083
|
-
|
|
4147
|
+
glGeometryBuffer = glContext.createBuffer();
|
|
4148
|
+
|
|
4149
|
+
// create the geometry buffer, triangle strip square
|
|
4150
|
+
const geometry = new Float32Array([glInstanceCount=0,0,1,0,0,1,1,1]);
|
|
4151
|
+
glContext.bindBuffer(gl_ARRAY_BUFFER, glGeometryBuffer);
|
|
4152
|
+
glContext.bufferData(gl_ARRAY_BUFFER, geometry, gl_STATIC_DRAW);
|
|
4084
4153
|
}
|
|
4085
4154
|
|
|
4086
4155
|
// Setup render each frame, called automatically by engine
|
|
4087
4156
|
function glPreRender()
|
|
4088
4157
|
{
|
|
4089
4158
|
// clear and set to same size as main canvas
|
|
4090
|
-
glContext.viewport(0, 0, glCanvas.width
|
|
4159
|
+
glContext.viewport(0, 0, glCanvas.width=mainCanvas.width, glCanvas.height=mainCanvas.height);
|
|
4091
4160
|
glContext.clear(gl_COLOR_BUFFER_BIT);
|
|
4092
4161
|
|
|
4093
4162
|
// set up the shader
|
|
4094
4163
|
glContext.useProgram(glShader);
|
|
4095
4164
|
glContext.activeTexture(gl_TEXTURE0);
|
|
4096
4165
|
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
|
-
|
|
4166
|
+
|
|
4101
4167
|
// set vertex attributes
|
|
4102
|
-
let offset = 0;
|
|
4103
|
-
|
|
4168
|
+
let offset = glAdditive = glBatchAdditive = 0;
|
|
4169
|
+
let initVertexAttribArray = (name, type, typeSize, size)=>
|
|
4104
4170
|
{
|
|
4105
4171
|
const location = glContext.getAttribLocation(glShader, name);
|
|
4172
|
+
const stride = typeSize && gl_INSTANCE_BYTE_STRIDE; // only if not geometry
|
|
4173
|
+
const divisor = typeSize && 1; // only if not geometry
|
|
4174
|
+
const normalize = typeSize==1; // only if color
|
|
4106
4175
|
glContext.enableVertexAttribArray(location);
|
|
4107
|
-
glContext.vertexAttribPointer(location, size, type, normalize,
|
|
4176
|
+
glContext.vertexAttribPointer(location, size, type, normalize, stride, offset);
|
|
4177
|
+
glContext.vertexAttribDivisor(location, divisor);
|
|
4108
4178
|
offset += size*typeSize;
|
|
4109
4179
|
}
|
|
4110
|
-
|
|
4111
|
-
initVertexAttribArray('
|
|
4112
|
-
|
|
4180
|
+
glContext.bindBuffer(gl_ARRAY_BUFFER, glGeometryBuffer);
|
|
4181
|
+
initVertexAttribArray('g', gl_FLOAT, 0, 2); // geometry
|
|
4182
|
+
glContext.bindBuffer(gl_ARRAY_BUFFER, glArrayBuffer);
|
|
4183
|
+
glContext.bufferData(gl_ARRAY_BUFFER, gl_INSTANCE_BUFFER_SIZE, gl_DYNAMIC_DRAW);
|
|
4184
|
+
initVertexAttribArray('p', gl_FLOAT, 4, 4); // position & size
|
|
4185
|
+
initVertexAttribArray('u', gl_FLOAT, 4, 4); // texture coords
|
|
4186
|
+
initVertexAttribArray('c', gl_UNSIGNED_BYTE, 1, 4); // color
|
|
4187
|
+
initVertexAttribArray('a', gl_UNSIGNED_BYTE, 1, 4); // additiveColor
|
|
4188
|
+
initVertexAttribArray('r', gl_FLOAT, 4, 1); // rotation
|
|
4113
4189
|
|
|
4114
4190
|
// build the transform matrix
|
|
4115
|
-
const
|
|
4116
|
-
const
|
|
4117
|
-
|
|
4118
|
-
const cy = -1 - sy*cameraPos.y;
|
|
4119
|
-
glContext.uniformMatrix4fv(glContext.getUniformLocation(glShader, 'm'), 0,
|
|
4191
|
+
const s = vec2(2*cameraScale).divide(mainCanvasSize);
|
|
4192
|
+
const p = vec2(-1).subtract(cameraPos.multiply(s));
|
|
4193
|
+
glContext.uniformMatrix4fv(glContext.getUniformLocation(glShader, 'm'), false,
|
|
4120
4194
|
new Float32Array([
|
|
4121
|
-
|
|
4122
|
-
|
|
4123
|
-
|
|
4124
|
-
|
|
4195
|
+
s.x, 0, 0, 0,
|
|
4196
|
+
0, s.y, 0, 0,
|
|
4197
|
+
1, 1, 1, 1,
|
|
4198
|
+
p.x, p.y, 0, 0
|
|
4125
4199
|
])
|
|
4126
4200
|
);
|
|
4127
4201
|
}
|
|
@@ -4142,7 +4216,7 @@ function glSetTexture(texture)
|
|
|
4142
4216
|
|
|
4143
4217
|
/** Compile WebGL shader of the given type, will throw errors if in debug mode
|
|
4144
4218
|
* @param {String} source
|
|
4145
|
-
* @param
|
|
4219
|
+
* @param {Number} type
|
|
4146
4220
|
* @return {WebGLShader}
|
|
4147
4221
|
* @memberof WebGL */
|
|
4148
4222
|
function glCompileShader(source, type)
|
|
@@ -4159,8 +4233,8 @@ function glCompileShader(source, type)
|
|
|
4159
4233
|
}
|
|
4160
4234
|
|
|
4161
4235
|
/** Create WebGL program with given shaders
|
|
4162
|
-
* @param {
|
|
4163
|
-
* @param {
|
|
4236
|
+
* @param {String} vsSource
|
|
4237
|
+
* @param {String} fsSource
|
|
4164
4238
|
* @return {WebGLProgram}
|
|
4165
4239
|
* @memberof WebGL */
|
|
4166
4240
|
function glCreateProgram(vsSource, fsSource)
|
|
@@ -4178,7 +4252,7 @@ function glCreateProgram(vsSource, fsSource)
|
|
|
4178
4252
|
}
|
|
4179
4253
|
|
|
4180
4254
|
/** Create WebGL texture from an image and init the texture settings
|
|
4181
|
-
* @param {
|
|
4255
|
+
* @param {HTMLImageElement} image
|
|
4182
4256
|
* @return {WebGLTexture}
|
|
4183
4257
|
* @memberof WebGL */
|
|
4184
4258
|
function glCreateTexture(image)
|
|
@@ -4188,7 +4262,7 @@ function glCreateTexture(image)
|
|
|
4188
4262
|
glContext.bindTexture(gl_TEXTURE_2D, texture);
|
|
4189
4263
|
if (image)
|
|
4190
4264
|
glContext.texImage2D(gl_TEXTURE_2D, 0, gl_RGBA, gl_RGBA, gl_UNSIGNED_BYTE, image);
|
|
4191
|
-
|
|
4265
|
+
|
|
4192
4266
|
// use point filtering for pixelated rendering
|
|
4193
4267
|
const filter = canvasPixelated ? gl_NEAREST : gl_LINEAR;
|
|
4194
4268
|
glContext.texParameteri(gl_TEXTURE_2D, gl_TEXTURE_MIN_FILTER, filter);
|
|
@@ -4203,29 +4277,31 @@ function glCreateTexture(image)
|
|
|
4203
4277
|
* @memberof WebGL */
|
|
4204
4278
|
function glFlush()
|
|
4205
4279
|
{
|
|
4206
|
-
if (!
|
|
4280
|
+
if (!glInstanceCount) return;
|
|
4207
4281
|
|
|
4208
4282
|
const destBlend = glBatchAdditive ? gl_ONE : gl_ONE_MINUS_SRC_ALPHA;
|
|
4209
4283
|
glContext.blendFuncSeparate(gl_SRC_ALPHA, destBlend, gl_ONE, destBlend);
|
|
4210
4284
|
glContext.enable(gl_BLEND);
|
|
4211
4285
|
|
|
4212
4286
|
// draw all the sprites in the batch and reset the buffer
|
|
4213
|
-
glContext.bufferSubData(gl_ARRAY_BUFFER, 0,
|
|
4214
|
-
glContext.
|
|
4215
|
-
|
|
4287
|
+
glContext.bufferSubData(gl_ARRAY_BUFFER, 0, glPositionData);
|
|
4288
|
+
glContext.drawArraysInstanced(gl_TRIANGLE_STRIP, 0, 4, glInstanceCount);
|
|
4289
|
+
if (showWatermark)
|
|
4290
|
+
drawCount += glInstanceCount;
|
|
4291
|
+
glInstanceCount = 0;
|
|
4216
4292
|
glBatchAdditive = glAdditive;
|
|
4217
4293
|
}
|
|
4218
4294
|
|
|
4219
4295
|
/** Draw any sprites still in the buffer, copy to main canvas and clear
|
|
4220
4296
|
* @param {CanvasRenderingContext2D} context
|
|
4221
|
-
* @param {Boolean} [forceDraw
|
|
4297
|
+
* @param {Boolean} [forceDraw]
|
|
4222
4298
|
* @memberof WebGL */
|
|
4223
|
-
function glCopyToContext(context, forceDraw)
|
|
4299
|
+
function glCopyToContext(context, forceDraw=false)
|
|
4224
4300
|
{
|
|
4225
|
-
if (!
|
|
4226
|
-
|
|
4301
|
+
if (!glInstanceCount && !forceDraw) return;
|
|
4302
|
+
|
|
4227
4303
|
glFlush();
|
|
4228
|
-
|
|
4304
|
+
|
|
4229
4305
|
// do not draw in overlay mode because the canvas is visible
|
|
4230
4306
|
if (!glOverlay || forceDraw)
|
|
4231
4307
|
context.drawImage(glCanvas, 0, 0);
|
|
@@ -4246,61 +4322,25 @@ function glCopyToContext(context, forceDraw)
|
|
|
4246
4322
|
* @memberof WebGL */
|
|
4247
4323
|
function glDraw(x, y, sizeX, sizeY, angle, uv0X, uv0Y, uv1X, uv1Y, rgba, rgbaAdditive=0)
|
|
4248
4324
|
{
|
|
4249
|
-
|
|
4250
|
-
const vertCount = 6;
|
|
4251
|
-
if (glBatchCount >= gl_MAX_BATCH-vertCount || glBatchAdditive != glAdditive)
|
|
4252
|
-
glFlush();
|
|
4253
|
-
|
|
4254
|
-
// prepare to create the verts from size and angle
|
|
4255
|
-
const c = Math.cos(angle)/2, s = Math.sin(angle)/2;
|
|
4256
|
-
const cx = c*sizeX, cy = c*sizeY, sx = s*sizeX, sy = s*sizeY;
|
|
4257
|
-
const positionData =
|
|
4258
|
-
[
|
|
4259
|
-
x-cx+sy, y+cy+sx, uv0X, uv0Y,
|
|
4260
|
-
x-cx-sy, y-cy+sx, uv0X, uv1Y,
|
|
4261
|
-
x+cx+sy, y+cy-sx, uv1X, uv0Y,
|
|
4262
|
-
x+cx-sy, y-cy-sx, uv1X, uv1Y,
|
|
4263
|
-
];
|
|
4264
|
-
|
|
4265
|
-
// setup 2 triangle strip quad
|
|
4266
|
-
for(let i = vertCount, offset = glBatchCount * gl_INDICIES_PER_VERT; i--;)
|
|
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
|
-
}
|
|
4325
|
+
ASSERT(typeof rgba == 'number' && typeof rgbaAdditive == 'number', 'invalid color');
|
|
4278
4326
|
|
|
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
4327
|
// flush if there is not enough room or if different blend mode
|
|
4286
|
-
|
|
4287
|
-
if (glBatchCount >= gl_MAX_BATCH-vertCount || glBatchAdditive != glAdditive)
|
|
4328
|
+
if (glInstanceCount >= gl_MAX_INSTANCES-1 || glBatchAdditive != glAdditive)
|
|
4288
4329
|
glFlush();
|
|
4289
|
-
|
|
4290
|
-
|
|
4291
|
-
|
|
4292
|
-
|
|
4293
|
-
|
|
4294
|
-
|
|
4295
|
-
|
|
4296
|
-
|
|
4297
|
-
|
|
4298
|
-
|
|
4299
|
-
|
|
4300
|
-
|
|
4301
|
-
|
|
4302
|
-
|
|
4303
|
-
glBatchCount += vertCount;
|
|
4330
|
+
|
|
4331
|
+
let offset = glInstanceCount * gl_INDICIES_PER_INSTANCE;
|
|
4332
|
+
glPositionData[offset++] = x;
|
|
4333
|
+
glPositionData[offset++] = y;
|
|
4334
|
+
glPositionData[offset++] = sizeX;
|
|
4335
|
+
glPositionData[offset++] = sizeY;
|
|
4336
|
+
glPositionData[offset++] = uv0X;
|
|
4337
|
+
glPositionData[offset++] = uv0Y;
|
|
4338
|
+
glPositionData[offset++] = uv1X;
|
|
4339
|
+
glPositionData[offset++] = uv1Y;
|
|
4340
|
+
glColorData[offset++] = rgba;
|
|
4341
|
+
glColorData[offset++] = rgbaAdditive;
|
|
4342
|
+
glPositionData[offset++] = angle;
|
|
4343
|
+
glInstanceCount++;
|
|
4304
4344
|
}
|
|
4305
4345
|
|
|
4306
4346
|
///////////////////////////////////////////////////////////////////////////////
|
|
@@ -4312,9 +4352,9 @@ let glPostShader, glPostArrayBuffer, glPostTexture, glPostIncludeOverlay;
|
|
|
4312
4352
|
* @param {String} shaderCode
|
|
4313
4353
|
* @param {Boolean} includeOverlay
|
|
4314
4354
|
* @memberof WebGL */
|
|
4315
|
-
function glInitPostProcess(shaderCode, includeOverlay)
|
|
4355
|
+
function glInitPostProcess(shaderCode, includeOverlay=false)
|
|
4316
4356
|
{
|
|
4317
|
-
ASSERT(!glPostShader
|
|
4357
|
+
ASSERT(!glPostShader, 'can only have 1 post effects shader');
|
|
4318
4358
|
|
|
4319
4359
|
if (!shaderCode) // default shader pass through
|
|
4320
4360
|
shaderCode = 'void mainImage(out vec4 c,vec2 p){c=texture(iChannel0,p/iResolution.xy);}';
|
|
@@ -4325,14 +4365,14 @@ function glInitPostProcess(shaderCode, includeOverlay)
|
|
|
4325
4365
|
'precision highp float;'+ // use highp for better accuracy
|
|
4326
4366
|
'in vec2 p;'+ // position
|
|
4327
4367
|
'void main(){'+ // shader entry point
|
|
4328
|
-
'gl_Position=vec4(p
|
|
4368
|
+
'gl_Position=vec4(p+p-1.,1,1);'+ // set position
|
|
4329
4369
|
'}' // end of shader
|
|
4330
4370
|
,
|
|
4331
4371
|
'#version 300 es\n' + // specify GLSL ES version
|
|
4332
4372
|
'precision highp float;'+ // use highp for better accuracy
|
|
4333
4373
|
'uniform sampler2D iChannel0;'+ // input texture
|
|
4334
4374
|
'uniform vec3 iResolution;'+ // size of output texture
|
|
4335
|
-
'uniform float iTime;'+ // time
|
|
4375
|
+
'uniform float iTime;'+ // time
|
|
4336
4376
|
'out vec4 c;'+ // out color
|
|
4337
4377
|
'\n' + shaderCode + '\n'+ // insert custom shader code
|
|
4338
4378
|
'void main(){'+ // shader entry point
|
|
@@ -4343,11 +4383,13 @@ function glInitPostProcess(shaderCode, includeOverlay)
|
|
|
4343
4383
|
|
|
4344
4384
|
// create buffer and texture
|
|
4345
4385
|
glPostArrayBuffer = glContext.createBuffer();
|
|
4346
|
-
glPostTexture = glCreateTexture();
|
|
4386
|
+
glPostTexture = glCreateTexture(undefined);
|
|
4347
4387
|
glPostIncludeOverlay = includeOverlay;
|
|
4348
4388
|
|
|
4349
4389
|
// hide the original 2d canvas
|
|
4350
4390
|
mainCanvas.style.visibility = 'hidden';
|
|
4391
|
+
if (glPostIncludeOverlay)
|
|
4392
|
+
overlayCanvas.style.visibility = 'hidden';
|
|
4351
4393
|
}
|
|
4352
4394
|
|
|
4353
4395
|
// Render the post processing shader, called automatically by the engine
|
|
@@ -4368,21 +4410,14 @@ function glRenderPostProcess()
|
|
|
4368
4410
|
glContext.viewport(0, 0, glCanvas.width = mainCanvas.width, glCanvas.height = mainCanvas.height);
|
|
4369
4411
|
}
|
|
4370
4412
|
|
|
4371
|
-
|
|
4372
|
-
|
|
4373
|
-
// copy overlay canvas so it will be included in post processing
|
|
4374
|
-
mainContext.drawImage(overlayCanvas, 0, 0);
|
|
4375
|
-
|
|
4376
|
-
// clear overlay canvas
|
|
4377
|
-
overlayCanvas.width = mainCanvas.width;
|
|
4378
|
-
}
|
|
4413
|
+
// copy overlay canvas so it will be included in post processing
|
|
4414
|
+
glPostIncludeOverlay && mainContext.drawImage(overlayCanvas, 0, 0);
|
|
4379
4415
|
|
|
4380
4416
|
// setup shader program to draw one triangle
|
|
4381
4417
|
glContext.useProgram(glPostShader);
|
|
4418
|
+
glContext.bindBuffer(gl_ARRAY_BUFFER, glGeometryBuffer);
|
|
4419
|
+
glContext.pixelStorei(gl_UNPACK_FLIP_Y_WEBGL, 1);
|
|
4382
4420
|
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
4421
|
|
|
4387
4422
|
// set textures, pass in the 2d canvas and gl canvas in separate texture channels
|
|
4388
4423
|
glContext.activeTexture(gl_TEXTURE0);
|
|
@@ -4393,19 +4428,19 @@ function glRenderPostProcess()
|
|
|
4393
4428
|
const vertexByteStride = 8;
|
|
4394
4429
|
const pLocation = glContext.getAttribLocation(glPostShader, 'p');
|
|
4395
4430
|
glContext.enableVertexAttribArray(pLocation);
|
|
4396
|
-
glContext.vertexAttribPointer(pLocation, 2, gl_FLOAT,
|
|
4431
|
+
glContext.vertexAttribPointer(pLocation, 2, gl_FLOAT, false, vertexByteStride, 0);
|
|
4397
4432
|
|
|
4398
4433
|
// set uniforms and draw
|
|
4399
4434
|
const uniformLocation = (name)=>glContext.getUniformLocation(glPostShader, name);
|
|
4400
4435
|
glContext.uniform1i(uniformLocation('iChannel0'), 0);
|
|
4401
4436
|
glContext.uniform1f(uniformLocation('iTime'), time);
|
|
4402
4437
|
glContext.uniform3f(uniformLocation('iResolution'), mainCanvas.width, mainCanvas.height, 1);
|
|
4403
|
-
glContext.drawArrays(gl_TRIANGLE_STRIP, 0,
|
|
4438
|
+
glContext.drawArrays(gl_TRIANGLE_STRIP, 0, 4);
|
|
4404
4439
|
}
|
|
4405
4440
|
|
|
4406
4441
|
///////////////////////////////////////////////////////////////////////////////
|
|
4407
4442
|
// store gl constants as integers so their name doesn't use space in minifed
|
|
4408
|
-
const
|
|
4443
|
+
const
|
|
4409
4444
|
gl_ONE = 1,
|
|
4410
4445
|
gl_TRIANGLE_STRIP = 5,
|
|
4411
4446
|
gl_SRC_ALPHA = 770,
|
|
@@ -4427,17 +4462,17 @@ gl_TEXTURE0 = 33984,
|
|
|
4427
4462
|
gl_ARRAY_BUFFER = 34962,
|
|
4428
4463
|
gl_STATIC_DRAW = 35044,
|
|
4429
4464
|
gl_DYNAMIC_DRAW = 35048,
|
|
4430
|
-
gl_FRAGMENT_SHADER = 35632,
|
|
4465
|
+
gl_FRAGMENT_SHADER = 35632,
|
|
4431
4466
|
gl_VERTEX_SHADER = 35633,
|
|
4432
4467
|
gl_COMPILE_STATUS = 35713,
|
|
4433
4468
|
gl_LINK_STATUS = 35714,
|
|
4434
4469
|
gl_UNPACK_FLIP_Y_WEBGL = 37440,
|
|
4435
4470
|
|
|
4436
4471
|
// constants for batch rendering
|
|
4437
|
-
|
|
4438
|
-
|
|
4439
|
-
|
|
4440
|
-
|
|
4472
|
+
gl_INDICIES_PER_INSTANCE = 11,
|
|
4473
|
+
gl_MAX_INSTANCES = 1e4,
|
|
4474
|
+
gl_INSTANCE_BYTE_STRIDE = gl_INDICIES_PER_INSTANCE * 4, // 11 * 4
|
|
4475
|
+
gl_INSTANCE_BUFFER_SIZE = gl_MAX_INSTANCES * gl_INSTANCE_BYTE_STRIDE;
|
|
4441
4476
|
/**
|
|
4442
4477
|
* LittleJS - The Tiny JavaScript Game Engine That Can!
|
|
4443
4478
|
* MIT License - Copyright 2021 Frank Force
|
|
@@ -4470,7 +4505,7 @@ const engineName = 'LittleJS';
|
|
|
4470
4505
|
* @type {String}
|
|
4471
4506
|
* @default
|
|
4472
4507
|
* @memberof Engine */
|
|
4473
|
-
const engineVersion = '1.
|
|
4508
|
+
const engineVersion = '1.9.1';
|
|
4474
4509
|
|
|
4475
4510
|
/** Frames per second to update objects
|
|
4476
4511
|
* @type {Number}
|
|
@@ -4511,14 +4546,14 @@ let timeReal = 0;
|
|
|
4511
4546
|
|
|
4512
4547
|
/** Is the game paused? Causes time and objects to not be updated
|
|
4513
4548
|
* @type {Boolean}
|
|
4514
|
-
* @default
|
|
4549
|
+
* @default false
|
|
4515
4550
|
* @memberof Engine */
|
|
4516
|
-
let paused =
|
|
4551
|
+
let paused = false;
|
|
4517
4552
|
|
|
4518
4553
|
/** Set if game is paused
|
|
4519
|
-
* @param {Boolean}
|
|
4554
|
+
* @param {Boolean} isPaused
|
|
4520
4555
|
* @memberof Engine */
|
|
4521
|
-
function setPaused(
|
|
4556
|
+
function setPaused(isPaused) { paused = isPaused; }
|
|
4522
4557
|
|
|
4523
4558
|
// Frame time tracking
|
|
4524
4559
|
let frameTimeLastMS = 0, frameTimeBufferMS = 0, averageFPS = 0;
|
|
@@ -4535,7 +4570,7 @@ let frameTimeLastMS = 0, frameTimeBufferMS = 0, averageFPS = 0;
|
|
|
4535
4570
|
* @memberof Engine */
|
|
4536
4571
|
function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRenderPost, imageSources=['tiles.png'])
|
|
4537
4572
|
{
|
|
4538
|
-
ASSERT(Array.isArray(imageSources)
|
|
4573
|
+
ASSERT(Array.isArray(imageSources), 'pass in images as array');
|
|
4539
4574
|
|
|
4540
4575
|
// internal update loop for engine
|
|
4541
4576
|
function engineUpdate(frameTimeMS=0)
|
|
@@ -4545,12 +4580,12 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
4545
4580
|
frameTimeLastMS = frameTimeMS;
|
|
4546
4581
|
if (debug || showWatermark)
|
|
4547
4582
|
averageFPS = lerp(.05, averageFPS, 1e3/(frameTimeDeltaMS||1));
|
|
4548
|
-
const debugSpeedUp = debug && keyIsDown(
|
|
4549
|
-
const debugSpeedDown = debug && keyIsDown(
|
|
4583
|
+
const debugSpeedUp = debug && keyIsDown('Equal'); // +
|
|
4584
|
+
const debugSpeedDown = debug && keyIsDown('Minus'); // -
|
|
4550
4585
|
if (debug) // +/- to speed/slow time
|
|
4551
4586
|
frameTimeDeltaMS *= debugSpeedUp ? 5 : debugSpeedDown ? .2 : 1;
|
|
4552
4587
|
timeReal += frameTimeDeltaMS / 1e3;
|
|
4553
|
-
frameTimeBufferMS +=
|
|
4588
|
+
frameTimeBufferMS += paused ? 0 : frameTimeDeltaMS;
|
|
4554
4589
|
if (!debugSpeedUp)
|
|
4555
4590
|
frameTimeBufferMS = min(frameTimeBufferMS, 50); // clamp incase of slow framerate
|
|
4556
4591
|
|
|
@@ -4660,7 +4695,7 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
4660
4695
|
'user-select:none;' + // prevent mobile hold to select
|
|
4661
4696
|
'-webkit-user-select:none;' + // compatibility for ios
|
|
4662
4697
|
'-webkit-touch-callout:none'; // compatibility for ios
|
|
4663
|
-
document.body.style = styleBody;
|
|
4698
|
+
document.body.style.cssText = styleBody;
|
|
4664
4699
|
document.body.appendChild(mainCanvas = document.createElement('canvas'));
|
|
4665
4700
|
mainContext = mainCanvas.getContext('2d');
|
|
4666
4701
|
|
|
@@ -4673,10 +4708,9 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
4673
4708
|
overlayContext = overlayCanvas.getContext('2d');
|
|
4674
4709
|
|
|
4675
4710
|
// set canvas style
|
|
4676
|
-
const styleCanvas =
|
|
4677
|
-
'position:absolute;' + // position
|
|
4711
|
+
const styleCanvas = 'position:absolute;' + // position
|
|
4678
4712
|
'top:50%;left:50%;transform:translate(-50%,-50%)'; // center
|
|
4679
|
-
(glCanvas||mainCanvas).style = mainCanvas.style = overlayCanvas.style = styleCanvas;
|
|
4713
|
+
(glCanvas||mainCanvas).style.cssText = mainCanvas.style.cssText = overlayCanvas.style.cssText = styleCanvas;
|
|
4680
4714
|
|
|
4681
4715
|
// create promises for loading images
|
|
4682
4716
|
const promises = imageSources.map((src, textureIndex)=>
|
|
@@ -4696,13 +4730,13 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
4696
4730
|
showSplashScreen && promises.push(new Promise(resolve =>
|
|
4697
4731
|
{
|
|
4698
4732
|
let t = 0;
|
|
4699
|
-
console.log(
|
|
4733
|
+
console.log(`${engineName} Engine v${engineVersion}`);
|
|
4700
4734
|
updateSplash();
|
|
4701
4735
|
function updateSplash()
|
|
4702
4736
|
{
|
|
4703
4737
|
clearInput();
|
|
4704
4738
|
drawEngineSplashScreen(t+=.01);
|
|
4705
|
-
t>1 ? resolve() : setTimeout(updateSplash,16);
|
|
4739
|
+
t>1 ? resolve() : setTimeout(updateSplash, 16);
|
|
4706
4740
|
}
|
|
4707
4741
|
}));
|
|
4708
4742
|
|
|
@@ -4763,7 +4797,7 @@ function engineObjectsDestroy()
|
|
|
4763
4797
|
|
|
4764
4798
|
/** Triggers a callback for each object within a given area
|
|
4765
4799
|
* @param {Vector2} [pos] - Center of test area
|
|
4766
|
-
* @param {Number} [size]
|
|
4800
|
+
* @param {Number|Vector2} [size] - Radius of circle if float, rectangle size if Vector2
|
|
4767
4801
|
* @param {Function} [callbackFunction] - Calls this function on every object that passes the test
|
|
4768
4802
|
* @param {Array} [objects=engineObjects] - List of objects to check
|
|
4769
4803
|
* @memberof Engine */
|
|
@@ -4774,7 +4808,7 @@ function engineObjectsCallback(pos, size, callbackFunction, objects=engineObject
|
|
|
4774
4808
|
for (const o of objects)
|
|
4775
4809
|
callbackFunction(o);
|
|
4776
4810
|
}
|
|
4777
|
-
else if (size
|
|
4811
|
+
else if (typeof size === 'object') // bounding box test
|
|
4778
4812
|
{
|
|
4779
4813
|
for (const o of objects)
|
|
4780
4814
|
isOverlapping(pos, size, o.pos, o.size) && callbackFunction(o);
|
|
@@ -4792,23 +4826,23 @@ function engineObjectsCallback(pos, size, callbackFunction, objects=engineObject
|
|
|
4792
4826
|
|
|
4793
4827
|
function drawEngineSplashScreen(t)
|
|
4794
4828
|
{
|
|
4795
|
-
const x =
|
|
4796
|
-
const w =
|
|
4797
|
-
const h =
|
|
4829
|
+
const x = overlayContext;
|
|
4830
|
+
const w = overlayCanvas.width = innerWidth;
|
|
4831
|
+
const h = overlayCanvas.height = innerHeight;
|
|
4832
|
+
|
|
4798
4833
|
{
|
|
4799
4834
|
// background
|
|
4800
4835
|
const p3 = percent(t, 1, .8);
|
|
4801
4836
|
const p4 = percent(t, 0, .5);
|
|
4802
4837
|
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));
|
|
4838
|
+
g.addColorStop(0,hsl(0,0,lerp(p4,0,p3/2),p3).toString());
|
|
4839
|
+
g.addColorStop(1,hsl(0,0,0,p3).toString());
|
|
4805
4840
|
x.save();
|
|
4806
4841
|
x.fillStyle = g;
|
|
4807
4842
|
x.fillRect(0,0,w,h);
|
|
4808
4843
|
}
|
|
4809
4844
|
|
|
4810
4845
|
// draw LittleJS logo...
|
|
4811
|
-
|
|
4812
4846
|
const rect = (X, Y, W, H, C)=>
|
|
4813
4847
|
{
|
|
4814
4848
|
x.beginPath();
|
|
@@ -4833,7 +4867,7 @@ function drawEngineSplashScreen(t)
|
|
|
4833
4867
|
C ? x.fill() : x.stroke();
|
|
4834
4868
|
};
|
|
4835
4869
|
const color = (c=0, l=0) =>
|
|
4836
|
-
hsl([.98,.3,.57,.14][c%4]-10,.8,[0,.3,.5,.8,.9][l]);
|
|
4870
|
+
hsl([.98,.3,.57,.14][c%4]-10,.8,[0,.3,.5,.8,.9][l]).toString();
|
|
4837
4871
|
const alpha = wave(1,1,t);
|
|
4838
4872
|
const p = percent(alpha, .1, .5);
|
|
4839
4873
|
|
|
@@ -4843,7 +4877,7 @@ function drawEngineSplashScreen(t)
|
|
|
4843
4877
|
x.scale(size,size);
|
|
4844
4878
|
x.translate(-40,-35);
|
|
4845
4879
|
x.lineJoin = x.lineCap = 'round';
|
|
4846
|
-
x.lineWidth = 1+p;
|
|
4880
|
+
x.lineWidth = .1 + p*1.9;
|
|
4847
4881
|
|
|
4848
4882
|
// drawing effect
|
|
4849
4883
|
const p2 = percent(alpha,.1,1);
|
|
@@ -4868,7 +4902,7 @@ function drawEngineSplashScreen(t)
|
|
|
4868
4902
|
|
|
4869
4903
|
// little stack
|
|
4870
4904
|
rect(37,14,9,6,color(3,2));
|
|
4871
|
-
rect(37,14,4,6,color(3,3));
|
|
4905
|
+
rect(37,14,4.5,6,color(3,3));
|
|
4872
4906
|
rect(37,14,9,6);
|
|
4873
4907
|
|
|
4874
4908
|
// big stack
|
|
@@ -4912,7 +4946,8 @@ function drawEngineSplashScreen(t)
|
|
|
4912
4946
|
x.lineTo(53+(1+i*2.9)*p,40);
|
|
4913
4947
|
x.lineTo(53+(4+i*3.5)*p,54);
|
|
4914
4948
|
x.fillStyle = color(0,i%2+2);
|
|
4915
|
-
x.fill()
|
|
4949
|
+
x.fill();
|
|
4950
|
+
i%2 && x.stroke();
|
|
4916
4951
|
}
|
|
4917
4952
|
|
|
4918
4953
|
// wheels
|
|
@@ -4935,7 +4970,7 @@ function drawEngineSplashScreen(t)
|
|
|
4935
4970
|
x.font = '900 16px arial';
|
|
4936
4971
|
x.textAlign = 'center';
|
|
4937
4972
|
x.textBaseline = 'top';
|
|
4938
|
-
x.lineWidth = 1+p*3;
|
|
4973
|
+
x.lineWidth = .1+p*3.9;
|
|
4939
4974
|
let w2 = 0;
|
|
4940
4975
|
for (let i=0; i<s.length; ++i)
|
|
4941
4976
|
w2 += x.measureText(s[i]).width;
|