littlejsengine 1.14.19 → 1.14.23
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/dist/littlejs.d.ts +118 -18
- package/dist/littlejs.esm.js +506 -199
- package/dist/littlejs.esm.min.js +1 -1
- package/dist/littlejs.js +493 -199
- package/dist/littlejs.min.js +1 -1
- package/dist/littlejs.release.js +468 -200
- package/examples/electron/game.js +1 -41
- package/examples/electron/index.html +2 -2
- package/examples/htmlMenu/game.js +1 -1
- package/examples/index.html +12 -6
- package/examples/module/game.js +5 -7
- package/examples/particles/index.html +6 -9
- package/examples/platformer/gameCharacter.js +1 -1
- package/examples/platformer/gameEffects.js +7 -2
- package/examples/platformer/gameObjects.js +3 -3
- package/examples/shorts/animation.js +2 -2
- package/examples/shorts/base.html +4 -1
- package/examples/shorts/cameraDrag.js +22 -0
- package/examples/shorts/debugDraw.js +38 -0
- package/examples/shorts/fps.js +90 -0
- package/examples/shorts/helloWorld.js +1 -1
- package/examples/shorts/hillGlideGame.js +1 -1
- package/examples/shorts/landerGame.js +1 -1
- package/examples/shorts/postProcess.js +1 -1
- package/examples/shorts/sequencer.js +3 -3
- package/examples/shorts/spaceGame.js +1 -1
- package/examples/shorts/spriteAtlas.js +6 -6
- package/examples/shorts/starfield.js +1 -1
- package/examples/shorts/tileRaycast.js +39 -0
- package/examples/shorts/tiles.png +0 -0
- package/examples/shorts/tiltedView.js +1 -1
- package/examples/starter/game.js +5 -7
- package/examples/starter/index.html +2 -2
- package/examples/typescript/game.js +3 -2
- package/examples/typescript/game.ts +5 -7
- package/examples/uiSystem/game.js +2 -2
- package/package.json +1 -1
- package/src/engine.js +3 -3
- package/src/engineAudio.js +13 -13
- package/src/engineDebug.js +27 -0
- package/src/engineDraw.js +68 -20
- package/src/engineExport.js +13 -0
- package/src/engineInput.js +14 -16
- package/src/engineObject.js +31 -6
- package/src/engineParticles.js +36 -15
- package/src/engineRelease.js +2 -1
- package/src/engineSettings.js +20 -1
- package/src/engineTileLayer.js +62 -59
- package/src/engineUtilities.js +206 -58
- package/src/engineWebGL.js +13 -8
- package/examples/shorts/raycasting.js +0 -79
package/dist/littlejs.release.js
CHANGED
|
@@ -33,7 +33,7 @@ const engineName = 'LittleJS';
|
|
|
33
33
|
* @type {string}
|
|
34
34
|
* @default
|
|
35
35
|
* @memberof Engine */
|
|
36
|
-
const engineVersion = '1.14.
|
|
36
|
+
const engineVersion = '1.14.24';
|
|
37
37
|
|
|
38
38
|
/** Frames per second to update
|
|
39
39
|
* @type {number}
|
|
@@ -516,7 +516,7 @@ function engineObjectsCollect(pos, size, objects=engineObjects)
|
|
|
516
516
|
|
|
517
517
|
/**
|
|
518
518
|
* @callback ObjectCallbackFunction - Function that processes an object
|
|
519
|
-
* @param {EngineObject}
|
|
519
|
+
* @param {EngineObject} object
|
|
520
520
|
* @memberof Engine
|
|
521
521
|
*/
|
|
522
522
|
|
|
@@ -564,7 +564,7 @@ function drawEngineSplashScreen(t)
|
|
|
564
564
|
// background
|
|
565
565
|
const p3 = percent(t, 1, .8);
|
|
566
566
|
const p4 = percent(t, 0, .5);
|
|
567
|
-
const g = x.createRadialGradient(w/2,h/2,0,w/2,h/2,
|
|
567
|
+
const g = x.createRadialGradient(w/2,h/2,0,w/2,h/2,hypot(w,h)*.7);
|
|
568
568
|
g.addColorStop(0,hsl(0,0,lerp(0,p3/2,p4),p3).toString());
|
|
569
569
|
g.addColorStop(1,hsl(0,0,0,p3).toString());
|
|
570
570
|
x.save();
|
|
@@ -755,7 +755,8 @@ function debugShowErrors(){}
|
|
|
755
755
|
function debugVideoCaptureIsActive(){ return false; }
|
|
756
756
|
function debugVideoCaptureStart (){}
|
|
757
757
|
function debugVideoCaptureStop (){}
|
|
758
|
-
function debugVideoCaptureUpdate(){}
|
|
758
|
+
function debugVideoCaptureUpdate(){}
|
|
759
|
+
function debugProtectConstant(o){ return o; }
|
|
759
760
|
/**
|
|
760
761
|
* LittleJS Utility Classes and Functions
|
|
761
762
|
* - General purpose math library
|
|
@@ -766,7 +767,7 @@ function debugVideoCaptureUpdate(){}
|
|
|
766
767
|
* @namespace Utilities
|
|
767
768
|
*/
|
|
768
769
|
|
|
769
|
-
/**
|
|
770
|
+
/** The value of PI
|
|
770
771
|
* @type {number}
|
|
771
772
|
* @default Math.PI
|
|
772
773
|
* @memberof Utilities */
|
|
@@ -776,25 +777,80 @@ const PI = Math.PI;
|
|
|
776
777
|
* @param {number} value
|
|
777
778
|
* @return {number}
|
|
778
779
|
* @memberof Utilities */
|
|
779
|
-
|
|
780
|
+
const abs = Math.abs;
|
|
781
|
+
|
|
782
|
+
/** Returns floored value of value passed in
|
|
783
|
+
* @param {number} value
|
|
784
|
+
* @return {number}
|
|
785
|
+
* @memberof Utilities */
|
|
786
|
+
const floor = Math.floor;
|
|
787
|
+
|
|
788
|
+
/** Returns ceiled value of value passed in
|
|
789
|
+
* @param {number} value
|
|
790
|
+
* @return {number}
|
|
791
|
+
* @memberof Utilities */
|
|
792
|
+
const ceil = Math.ceil;
|
|
793
|
+
|
|
794
|
+
/** Returns rounded value passed in
|
|
795
|
+
* @param {number} value
|
|
796
|
+
* @return {number}
|
|
797
|
+
* @memberof Utilities */
|
|
798
|
+
const round = Math.round;
|
|
780
799
|
|
|
781
800
|
/** Returns lowest value passed in
|
|
782
801
|
* @param {...number} values
|
|
783
802
|
* @return {number}
|
|
784
803
|
* @memberof Utilities */
|
|
785
|
-
|
|
804
|
+
const min = Math.min;
|
|
786
805
|
|
|
787
806
|
/** Returns highest value passed in
|
|
788
807
|
* @param {...number} values
|
|
789
808
|
* @return {number}
|
|
790
809
|
* @memberof Utilities */
|
|
791
|
-
|
|
810
|
+
const max = Math.max;
|
|
792
811
|
|
|
793
812
|
/** Returns the sign of value passed in
|
|
794
813
|
* @param {number} value
|
|
795
814
|
* @return {number}
|
|
796
815
|
* @memberof Utilities */
|
|
797
|
-
|
|
816
|
+
const sign = Math.sign;
|
|
817
|
+
|
|
818
|
+
/** Returns hypotenuse of values passed in
|
|
819
|
+
* @param {...number} values
|
|
820
|
+
* @return {number}
|
|
821
|
+
* @memberof Utilities */
|
|
822
|
+
const hypot = Math.hypot;
|
|
823
|
+
|
|
824
|
+
/** Returns log2 of value passed in
|
|
825
|
+
* @param {number} value
|
|
826
|
+
* @return {number}
|
|
827
|
+
* @memberof Utilities */
|
|
828
|
+
const log2 = Math.log2;
|
|
829
|
+
|
|
830
|
+
/** Returns sin of value passed in
|
|
831
|
+
* @param {number} value
|
|
832
|
+
* @return {number}
|
|
833
|
+
* @memberof Utilities */
|
|
834
|
+
const sin = Math.sin;
|
|
835
|
+
|
|
836
|
+
/** Returns cos of value passed in
|
|
837
|
+
* @param {number} value
|
|
838
|
+
* @return {number}
|
|
839
|
+
* @memberof Utilities */
|
|
840
|
+
const cos = Math.cos;
|
|
841
|
+
|
|
842
|
+
/** Returns tan of value passed in
|
|
843
|
+
* @param {number} value
|
|
844
|
+
* @return {number}
|
|
845
|
+
* @memberof Utilities */
|
|
846
|
+
const tan = Math.tan;
|
|
847
|
+
|
|
848
|
+
/** Returns atan2 of values passed in
|
|
849
|
+
* @param {number} y
|
|
850
|
+
* @param {number} x
|
|
851
|
+
* @return {number}
|
|
852
|
+
* @memberof Utilities */
|
|
853
|
+
const atan2 = Math.atan2;
|
|
798
854
|
|
|
799
855
|
/** Returns first parm modulo the second param, but adjusted so negative numbers work as expected
|
|
800
856
|
* @param {number} dividend
|
|
@@ -899,7 +955,7 @@ function isPowerOfTwo(value) { return !(value & (value - 1)); }
|
|
|
899
955
|
* @param {number} value
|
|
900
956
|
* @return {number}
|
|
901
957
|
* @memberof Utilities */
|
|
902
|
-
function nearestPowerOfTwo(value) { return 2**
|
|
958
|
+
function nearestPowerOfTwo(value) { return 2**ceil(log2(value)); }
|
|
903
959
|
|
|
904
960
|
/** Returns true if two axis aligned bounding boxes are overlapping
|
|
905
961
|
* this can be used for simple collision detection between objects
|
|
@@ -967,7 +1023,7 @@ function isIntersecting(start, end, pos, size)
|
|
|
967
1023
|
* @return {number} - Value waving between 0 and amplitude
|
|
968
1024
|
* @memberof Utilities */
|
|
969
1025
|
function wave(frequency=1, amplitude=1, t=time, offset=0)
|
|
970
|
-
{ return amplitude/2 * (1 -
|
|
1026
|
+
{ return amplitude/2 * (1 - cos(offset + t*frequency*2*PI)); }
|
|
971
1027
|
|
|
972
1028
|
/** Formats seconds to mm:ss style for display purposes
|
|
973
1029
|
* @param {number} t - time in seconds
|
|
@@ -1006,6 +1062,96 @@ function isNumber(n) { return typeof n === 'number' && !isNaN(n); }
|
|
|
1006
1062
|
* @memberof Utilities */
|
|
1007
1063
|
function isString(s) { return s !== undefined && s !== null && typeof s.toString() === 'string'; }
|
|
1008
1064
|
|
|
1065
|
+
/**
|
|
1066
|
+
* @callback LineTestFunction - Checks if a position is colliding
|
|
1067
|
+
* @param {Vector2} pos
|
|
1068
|
+
* @memberof Draw
|
|
1069
|
+
*/
|
|
1070
|
+
|
|
1071
|
+
/**
|
|
1072
|
+
* Casts a ray and returns position of the first collision found, or undefined if none are found
|
|
1073
|
+
* @param {Vector2} posStart
|
|
1074
|
+
* @param {Vector2} posEnd
|
|
1075
|
+
* @param {LineTestFunction} testFunction - Check if colliding
|
|
1076
|
+
* @param {Vector2} [normal] - Optional vector to store the normal
|
|
1077
|
+
* @return {Vector2|undefined} - Position of the collision or undefined if none found
|
|
1078
|
+
* @memberof Utilities */
|
|
1079
|
+
function lineTest(posStart, posEnd, testFunction, normal)
|
|
1080
|
+
{
|
|
1081
|
+
ASSERT(isVector2(posStart), 'posStart must be a vec2');
|
|
1082
|
+
ASSERT(isVector2(posEnd), 'posEnd must be a vec2');
|
|
1083
|
+
ASSERT(typeof testFunction === 'function', 'testFunction must be a function');
|
|
1084
|
+
ASSERT(!normal || isVector2(normal), 'normal must be a vec2');
|
|
1085
|
+
|
|
1086
|
+
// get ray direction and length
|
|
1087
|
+
const dx = posEnd.x - posStart.x;
|
|
1088
|
+
const dy = posEnd.y - posStart.y;
|
|
1089
|
+
const totalLength = hypot(dx, dy);
|
|
1090
|
+
if (!totalLength)
|
|
1091
|
+
return;
|
|
1092
|
+
|
|
1093
|
+
// current integer cell we are in
|
|
1094
|
+
const pos = posStart.floor();
|
|
1095
|
+
|
|
1096
|
+
// normalize ray direction
|
|
1097
|
+
const dirX = dx / totalLength;
|
|
1098
|
+
const dirY = dy / totalLength;
|
|
1099
|
+
|
|
1100
|
+
// step direction in grid
|
|
1101
|
+
const stepX = sign(dirX);
|
|
1102
|
+
const stepY = sign(dirY);
|
|
1103
|
+
|
|
1104
|
+
// distance along the ray to cross one full cell in X or Y
|
|
1105
|
+
const tDeltaX = dirX ? abs(1 / dirX) : Infinity;
|
|
1106
|
+
const tDeltaY = dirY ? abs(1 / dirY) : Infinity;
|
|
1107
|
+
|
|
1108
|
+
// distance along the ray from start to the first grid boundary
|
|
1109
|
+
const nextGridX = stepX > 0 ? pos.x + 1 : pos.x;
|
|
1110
|
+
const nextGridY = stepY > 0 ? pos.y + 1 : pos.y;
|
|
1111
|
+
const tMaxX = dirX ? (nextGridX - posStart.x) / dirX : Infinity;
|
|
1112
|
+
const tMaxY = dirY ? (nextGridY - posStart.y) / dirY : Infinity;
|
|
1113
|
+
|
|
1114
|
+
// use line drawing algorithm to test for collisions
|
|
1115
|
+
let t = 0, tX = tMaxX, tY = tMaxY, wasX = tDeltaX < tDeltaY;
|
|
1116
|
+
while (t < totalLength)
|
|
1117
|
+
{
|
|
1118
|
+
if (testFunction(pos))
|
|
1119
|
+
{
|
|
1120
|
+
// set hit point
|
|
1121
|
+
const hitPos = vec2(posStart.x + dirX*t, posStart.y + dirY*t);
|
|
1122
|
+
|
|
1123
|
+
// move inside of tile if on positive edge
|
|
1124
|
+
const e = 1e-9;
|
|
1125
|
+
if (wasX)
|
|
1126
|
+
{
|
|
1127
|
+
if (stepX < 0)
|
|
1128
|
+
hitPos.x -= e;
|
|
1129
|
+
}
|
|
1130
|
+
if (stepY < 0)
|
|
1131
|
+
hitPos.y -= e;
|
|
1132
|
+
|
|
1133
|
+
// set normal
|
|
1134
|
+
if (normal)
|
|
1135
|
+
wasX ? normal.set(-stepX,0) : normal.set(0,-stepY);
|
|
1136
|
+
return hitPos;
|
|
1137
|
+
}
|
|
1138
|
+
|
|
1139
|
+
// advance to the next grid boundary
|
|
1140
|
+
if (wasX = tX < tY)
|
|
1141
|
+
{
|
|
1142
|
+
pos.x += stepX;
|
|
1143
|
+
t = tX;
|
|
1144
|
+
tX += tDeltaX;
|
|
1145
|
+
}
|
|
1146
|
+
else
|
|
1147
|
+
{
|
|
1148
|
+
pos.y += stepY;
|
|
1149
|
+
t = tY;
|
|
1150
|
+
tY += tDeltaY;
|
|
1151
|
+
}
|
|
1152
|
+
}
|
|
1153
|
+
}
|
|
1154
|
+
|
|
1009
1155
|
///////////////////////////////////////////////////////////////////////////////
|
|
1010
1156
|
|
|
1011
1157
|
/** Random global functions
|
|
@@ -1024,7 +1170,7 @@ function rand(valueA=1, valueB=0) { return valueB + Math.random() * (valueA-valu
|
|
|
1024
1170
|
* @param {number} [valueB]
|
|
1025
1171
|
* @return {number}
|
|
1026
1172
|
* @memberof Random */
|
|
1027
|
-
function randInt(valueA, valueB=0) { return
|
|
1173
|
+
function randInt(valueA, valueB=0) { return floor(rand(valueA,valueB)); }
|
|
1028
1174
|
|
|
1029
1175
|
/** Randomly returns true or false given the chance of true passed in
|
|
1030
1176
|
* @param {number} [chance]
|
|
@@ -1103,7 +1249,7 @@ class RandomGenerator
|
|
|
1103
1249
|
* @param {number} valueA
|
|
1104
1250
|
* @param {number} [valueB]
|
|
1105
1251
|
* @return {number} */
|
|
1106
|
-
int(valueA, valueB=0) { return
|
|
1252
|
+
int(valueA, valueB=0) { return floor(this.float(valueA, valueB)); }
|
|
1107
1253
|
|
|
1108
1254
|
/** Randomly returns true or false given the chance of true passed in
|
|
1109
1255
|
* @param {number} [chance]
|
|
@@ -1130,6 +1276,39 @@ class RandomGenerator
|
|
|
1130
1276
|
* @return {Vector2} */
|
|
1131
1277
|
vec2(valueA=1, valueB=0)
|
|
1132
1278
|
{ return vec2(this.float(valueA, valueB), this.float(valueA, valueB)); }
|
|
1279
|
+
|
|
1280
|
+
/** Returns a random color between the two passed in colors, combine components if linear
|
|
1281
|
+
* @param {Color} [colorA=(1,1,1,1)]
|
|
1282
|
+
* @param {Color} [colorB=(0,0,0,1)]
|
|
1283
|
+
* @param {boolean} [linear]
|
|
1284
|
+
* @return {Color} */
|
|
1285
|
+
randColor(colorA=new Color, colorB=new Color(0,0,0,1), linear=false)
|
|
1286
|
+
{
|
|
1287
|
+
return linear ? colorA.lerp(colorB, this.float()) :
|
|
1288
|
+
new Color(
|
|
1289
|
+
this.float(colorA.r,colorB.r),
|
|
1290
|
+
this.float(colorA.g,colorB.g),
|
|
1291
|
+
this.float(colorA.b,colorB.b),
|
|
1292
|
+
this.float(colorA.a,colorB.a));
|
|
1293
|
+
}
|
|
1294
|
+
|
|
1295
|
+
/** Returns a new color that has each component randomly adjusted
|
|
1296
|
+
* @param {Color} color
|
|
1297
|
+
* @param {number} [amount]
|
|
1298
|
+
* @param {number} [alphaAmount]
|
|
1299
|
+
* @return {Color} */
|
|
1300
|
+
mutateColor(color, amount=.05, alphaAmount=0)
|
|
1301
|
+
{
|
|
1302
|
+
ASSERT_NUMBER_VALID(amount);
|
|
1303
|
+
ASSERT_NUMBER_VALID(alphaAmount);
|
|
1304
|
+
return new Color
|
|
1305
|
+
(
|
|
1306
|
+
color.r + this.float(amount, -amount),
|
|
1307
|
+
color.g + this.float(amount, -amount),
|
|
1308
|
+
color.b + this.float(amount, -amount),
|
|
1309
|
+
color.a + this.float(alphaAmount, -alphaAmount)
|
|
1310
|
+
).clamp();
|
|
1311
|
+
}
|
|
1133
1312
|
}
|
|
1134
1313
|
|
|
1135
1314
|
///////////////////////////////////////////////////////////////////////////////
|
|
@@ -1281,7 +1460,7 @@ class Vector2
|
|
|
1281
1460
|
|
|
1282
1461
|
/** Returns the clockwise angle of this vector, up is angle 0
|
|
1283
1462
|
* @return {number} */
|
|
1284
|
-
angle() { return
|
|
1463
|
+
angle() { return atan2(this.x, this.y); }
|
|
1285
1464
|
|
|
1286
1465
|
/** Sets this vector with clockwise angle and length passed in
|
|
1287
1466
|
* @param {number} [angle]
|
|
@@ -1291,8 +1470,8 @@ class Vector2
|
|
|
1291
1470
|
{
|
|
1292
1471
|
ASSERT_NUMBER_VALID(angle);
|
|
1293
1472
|
ASSERT_NUMBER_VALID(length);
|
|
1294
|
-
this.x = length*
|
|
1295
|
-
this.y = length*
|
|
1473
|
+
this.x = length*sin(angle);
|
|
1474
|
+
this.y = length*cos(angle);
|
|
1296
1475
|
return this;
|
|
1297
1476
|
}
|
|
1298
1477
|
|
|
@@ -1302,7 +1481,7 @@ class Vector2
|
|
|
1302
1481
|
rotate(angle)
|
|
1303
1482
|
{
|
|
1304
1483
|
ASSERT_NUMBER_VALID(angle);
|
|
1305
|
-
const c =
|
|
1484
|
+
const c = cos(-angle), s = sin(-angle);
|
|
1306
1485
|
return new Vector2(this.x*c - this.y*s, this.x*s + this.y*c);
|
|
1307
1486
|
}
|
|
1308
1487
|
|
|
@@ -1334,7 +1513,7 @@ class Vector2
|
|
|
1334
1513
|
|
|
1335
1514
|
/** Returns a copy of this vector with each axis floored
|
|
1336
1515
|
* @return {Vector2} */
|
|
1337
|
-
floor() { return new Vector2(
|
|
1516
|
+
floor() { return new Vector2(floor(this.x), floor(this.y)); }
|
|
1338
1517
|
|
|
1339
1518
|
/** Returns new vec2 with modded values
|
|
1340
1519
|
* @param {number} [divisor]
|
|
@@ -1642,67 +1821,67 @@ class Color
|
|
|
1642
1821
|
/** Color - White #ffffff
|
|
1643
1822
|
* @type {Color}
|
|
1644
1823
|
* @memberof Utilities */
|
|
1645
|
-
const WHITE =
|
|
1824
|
+
const WHITE = debugProtectConstant(rgb());
|
|
1646
1825
|
|
|
1647
1826
|
/** Color - Clear White #757474ff with 0 alpha
|
|
1648
1827
|
* @type {Color}
|
|
1649
1828
|
* @memberof Utilities */
|
|
1650
|
-
const CLEAR_WHITE =
|
|
1829
|
+
const CLEAR_WHITE = debugProtectConstant(rgb(1,1,1,0));
|
|
1651
1830
|
|
|
1652
1831
|
/** Color - Black #000000
|
|
1653
1832
|
* @type {Color}
|
|
1654
1833
|
* @memberof Utilities */
|
|
1655
|
-
const BLACK =
|
|
1834
|
+
const BLACK = debugProtectConstant(rgb(0,0,0));
|
|
1656
1835
|
|
|
1657
1836
|
/** Color - Clear Black #000000 with 0 alpha
|
|
1658
1837
|
* @type {Color}
|
|
1659
1838
|
* @memberof Utilities */
|
|
1660
|
-
const CLEAR_BLACK =
|
|
1839
|
+
const CLEAR_BLACK = debugProtectConstant(rgb(0,0,0,0));
|
|
1661
1840
|
|
|
1662
1841
|
/** Color - Gray #808080
|
|
1663
1842
|
* @type {Color}
|
|
1664
1843
|
* @memberof Utilities */
|
|
1665
|
-
const GRAY =
|
|
1844
|
+
const GRAY = debugProtectConstant(rgb(.5,.5,.5));
|
|
1666
1845
|
|
|
1667
1846
|
/** Color - Red #ff0000
|
|
1668
1847
|
* @type {Color}
|
|
1669
1848
|
* @memberof Utilities */
|
|
1670
|
-
const RED =
|
|
1849
|
+
const RED = debugProtectConstant(rgb(1,0,0));
|
|
1671
1850
|
|
|
1672
1851
|
/** Color - Orange #ff8000
|
|
1673
1852
|
* @type {Color}
|
|
1674
1853
|
* @memberof Utilities */
|
|
1675
|
-
const ORANGE =
|
|
1854
|
+
const ORANGE = debugProtectConstant(rgb(1,.5,0));
|
|
1676
1855
|
|
|
1677
1856
|
/** Color - Yellow #ffff00
|
|
1678
1857
|
* @type {Color}
|
|
1679
1858
|
* @memberof Utilities */
|
|
1680
|
-
const YELLOW =
|
|
1859
|
+
const YELLOW = debugProtectConstant(rgb(1,1,0));
|
|
1681
1860
|
|
|
1682
1861
|
/** Color - Green #00ff00
|
|
1683
1862
|
* @type {Color}
|
|
1684
1863
|
* @memberof Utilities */
|
|
1685
|
-
const GREEN =
|
|
1864
|
+
const GREEN = debugProtectConstant(rgb(0,1,0));
|
|
1686
1865
|
|
|
1687
1866
|
/** Color - Cyan #00ffff
|
|
1688
1867
|
* @type {Color}
|
|
1689
1868
|
* @memberof Utilities */
|
|
1690
|
-
const CYAN =
|
|
1869
|
+
const CYAN = debugProtectConstant(rgb(0,1,1));
|
|
1691
1870
|
|
|
1692
1871
|
/** Color - Blue #0000ff
|
|
1693
1872
|
* @type {Color}
|
|
1694
1873
|
* @memberof Utilities */
|
|
1695
|
-
const BLUE =
|
|
1874
|
+
const BLUE = debugProtectConstant(rgb(0,0,1));
|
|
1696
1875
|
|
|
1697
1876
|
/** Color - Purple #8000ff
|
|
1698
1877
|
* @type {Color}
|
|
1699
1878
|
* @memberof Utilities */
|
|
1700
|
-
const PURPLE =
|
|
1879
|
+
const PURPLE = debugProtectConstant(rgb(.5,0,1));
|
|
1701
1880
|
|
|
1702
1881
|
/** Color - Magenta #ff00ff
|
|
1703
1882
|
* @type {Color}
|
|
1704
1883
|
* @memberof Utilities */
|
|
1705
|
-
const MAGENTA =
|
|
1884
|
+
const MAGENTA = debugProtectConstant(rgb(1,0,1));
|
|
1706
1885
|
|
|
1707
1886
|
///////////////////////////////////////////////////////////////////////////////
|
|
1708
1887
|
|
|
@@ -1765,41 +1944,11 @@ class Timer
|
|
|
1765
1944
|
|
|
1766
1945
|
/** Returns this timer expressed as a string
|
|
1767
1946
|
* @return {string} */
|
|
1768
|
-
toString() { return this.isSet() ?
|
|
1947
|
+
toString() { return this.isSet() ? abs(this.get()) + ' seconds ' + (this.get()<0 ? 'before' : 'after' ) : 'unset'; }
|
|
1769
1948
|
|
|
1770
1949
|
/** Get how long since elapsed, returns 0 if not set (returns negative if currently active)
|
|
1771
1950
|
* @return {number} */
|
|
1772
1951
|
valueOf() { return this.get(); }
|
|
1773
|
-
}
|
|
1774
|
-
|
|
1775
|
-
///////////////////////////////////////////////////////////////////////////////
|
|
1776
|
-
// Helper functions used by the engine
|
|
1777
|
-
|
|
1778
|
-
// make color constants immutable with debug assertions
|
|
1779
|
-
function protectEngineConstant(obj)
|
|
1780
|
-
{
|
|
1781
|
-
if (debug)
|
|
1782
|
-
{
|
|
1783
|
-
// get properties and store original values
|
|
1784
|
-
const props = Object.keys(obj), values = {};
|
|
1785
|
-
props.forEach(prop => values[prop] = obj[prop]);
|
|
1786
|
-
|
|
1787
|
-
// replace with getters/setters that assert
|
|
1788
|
-
props.forEach(prop =>
|
|
1789
|
-
{
|
|
1790
|
-
Object.defineProperty(obj, prop, {
|
|
1791
|
-
get: () => values[prop],
|
|
1792
|
-
set: (value) =>
|
|
1793
|
-
{
|
|
1794
|
-
ASSERT(false, `Cannot modify engine constant. Attempted to set constant (${obj}) property '${prop}' to '${value}'.`);
|
|
1795
|
-
},
|
|
1796
|
-
enumerable: true
|
|
1797
|
-
});
|
|
1798
|
-
});
|
|
1799
|
-
}
|
|
1800
|
-
|
|
1801
|
-
// freeze the object to prevent adding new properties
|
|
1802
|
-
return Object.freeze(obj);
|
|
1803
1952
|
}
|
|
1804
1953
|
/**
|
|
1805
1954
|
* LittleJS Engine Settings
|
|
@@ -2017,6 +2166,14 @@ let touchInputEnable = true;
|
|
|
2017
2166
|
* @memberof Settings */
|
|
2018
2167
|
let touchGamepadEnable = false;
|
|
2019
2168
|
|
|
2169
|
+
/** True if touch gamepad should have start button in the center
|
|
2170
|
+
* - When the game is paused, any touch will press the button
|
|
2171
|
+
* - This can function as a way to pause/unpause the game
|
|
2172
|
+
* @type {boolean}
|
|
2173
|
+
* @default
|
|
2174
|
+
* @memberof Settings */
|
|
2175
|
+
let touchGamepadCenterButton = true;
|
|
2176
|
+
|
|
2020
2177
|
/** True if touch gamepad should be analog stick or false to use if 8 way dpad
|
|
2021
2178
|
* @type {boolean}
|
|
2022
2179
|
* @default
|
|
@@ -2192,9 +2349,14 @@ function setHeadlessMode(headless) { headlessMode = headless; }
|
|
|
2192
2349
|
* @memberof Settings */
|
|
2193
2350
|
function setGLEnable(enable)
|
|
2194
2351
|
{
|
|
2352
|
+
if (enable && !glCanBeEnabled)
|
|
2353
|
+
{
|
|
2354
|
+
console.warn('Can not enable WebGL if it was disabled on start.');
|
|
2355
|
+
return;
|
|
2356
|
+
}
|
|
2195
2357
|
glEnable = enable;
|
|
2196
2358
|
if (glCanvas) // hide glCanvas if WebGL is disabled
|
|
2197
|
-
glCanvas.style.
|
|
2359
|
+
glCanvas.style.display = enable ? '' : 'none';
|
|
2198
2360
|
}
|
|
2199
2361
|
|
|
2200
2362
|
/** Set how many sided polygons to use when drawing circles and ellipses with WebGL
|
|
@@ -2282,6 +2444,12 @@ function setTouchInputEnable(enable) { touchInputEnable = enable; }
|
|
|
2282
2444
|
* @memberof Settings */
|
|
2283
2445
|
function setTouchGamepadEnable(enable) { touchGamepadEnable = enable; }
|
|
2284
2446
|
|
|
2447
|
+
/** True if touch gamepad should have start button in the center
|
|
2448
|
+
* - This can function as a way to pause/unpause the game
|
|
2449
|
+
* @param {boolean} enable
|
|
2450
|
+
* @memberof Settings */
|
|
2451
|
+
function setTouchGamepadCenterButton(enable) { touchGamepadCenterButton = enable; }
|
|
2452
|
+
|
|
2285
2453
|
/** Set if touch gamepad should be analog stick or 8 way dpad
|
|
2286
2454
|
* @param {boolean} analog
|
|
2287
2455
|
* @memberof Settings */
|
|
@@ -2643,6 +2811,27 @@ class EngineObject
|
|
|
2643
2811
|
// test which side we bounced off (or both if a corner)
|
|
2644
2812
|
const blockedLayerY = tileCollisionTest(vec2(oldPos.x, this.pos.y), this.size, this);
|
|
2645
2813
|
const blockedLayerX = tileCollisionTest(vec2(this.pos.x, oldPos.y), this.size, this);
|
|
2814
|
+
|
|
2815
|
+
if (blockedLayerX)
|
|
2816
|
+
{
|
|
2817
|
+
// try to move up a tiny bit
|
|
2818
|
+
const epsilon = 1e-3;
|
|
2819
|
+
const maxMoveUp = .1;
|
|
2820
|
+
const y = floor(oldPos.y-this.size.y/2+1) +
|
|
2821
|
+
this.size.y/2 + epsilon;
|
|
2822
|
+
const delta = y - this.pos.y;
|
|
2823
|
+
if (delta < maxMoveUp)
|
|
2824
|
+
if (!tileCollisionTest(vec2(this.pos.x, y), this.size, this))
|
|
2825
|
+
{
|
|
2826
|
+
this.pos.y = y;
|
|
2827
|
+
debugPhysics && debugRect(this.pos, this.size, '#ff0');
|
|
2828
|
+
return;
|
|
2829
|
+
}
|
|
2830
|
+
|
|
2831
|
+
// move to previous position and bounce
|
|
2832
|
+
this.pos.x = oldPos.x;
|
|
2833
|
+
this.velocity.x *= -this.restitution;
|
|
2834
|
+
}
|
|
2646
2835
|
if (blockedLayerY || !blockedLayerX)
|
|
2647
2836
|
{
|
|
2648
2837
|
// bounce velocity
|
|
@@ -2666,12 +2855,6 @@ class EngineObject
|
|
|
2666
2855
|
this.groundObject = undefined;
|
|
2667
2856
|
}
|
|
2668
2857
|
}
|
|
2669
|
-
if (blockedLayerX)
|
|
2670
|
-
{
|
|
2671
|
-
// move to previous position and bounce
|
|
2672
|
-
this.pos.x = oldPos.x;
|
|
2673
|
-
this.velocity.x *= -this.restitution;
|
|
2674
|
-
}
|
|
2675
2858
|
debugPhysics && debugRect(this.pos, this.size, '#f00');
|
|
2676
2859
|
}
|
|
2677
2860
|
}
|
|
@@ -2729,6 +2912,16 @@ class EngineObject
|
|
|
2729
2912
|
*/
|
|
2730
2913
|
collideWithObject(object) { return true; }
|
|
2731
2914
|
|
|
2915
|
+
/** Get this object's up vector
|
|
2916
|
+
* @param {number} [scale] - length of the vector
|
|
2917
|
+
* @return {Vector2} */
|
|
2918
|
+
getUp(scale=1) { return vec2().setAngle(this.angle, scale); }
|
|
2919
|
+
|
|
2920
|
+
/** Get this object's right vector
|
|
2921
|
+
* @param {number} [scale] - length of the vector
|
|
2922
|
+
* @return {Vector2} */
|
|
2923
|
+
getRight(scale=1) { return vec2().setAngle(this.angle+PI/2, scale); }
|
|
2924
|
+
|
|
2732
2925
|
/** How long since the object was created
|
|
2733
2926
|
* @return {number} */
|
|
2734
2927
|
getAliveTime() { return time - this.spawnTime; }
|
|
@@ -3080,6 +3273,7 @@ function drawTile(pos, size=new Vector2(1), tileInfo, color=WHITE,
|
|
|
3080
3273
|
const bleedScale = tileInfo ? tileInfo.bleedScale : 0;
|
|
3081
3274
|
if (useWebGL)
|
|
3082
3275
|
{
|
|
3276
|
+
ASSERT(!!glContext, 'WebGL is not enabled!');
|
|
3083
3277
|
if (screenSpace)
|
|
3084
3278
|
{
|
|
3085
3279
|
// convert to world space
|
|
@@ -3175,6 +3369,7 @@ function drawRectGradient(pos, size, colorTop=WHITE, colorBottom=BLACK, angle=0,
|
|
|
3175
3369
|
ASSERT(!context || !useWebGL, 'context only supported in canvas 2D mode');
|
|
3176
3370
|
if (useWebGL)
|
|
3177
3371
|
{
|
|
3372
|
+
ASSERT(!!glContext, 'WebGL is not enabled!');
|
|
3178
3373
|
if (screenSpace)
|
|
3179
3374
|
{
|
|
3180
3375
|
// convert to world space
|
|
@@ -3186,7 +3381,7 @@ function drawRectGradient(pos, size, colorTop=WHITE, colorBottom=BLACK, angle=0,
|
|
|
3186
3381
|
const halfSizeX = size.x/2, halfSizeY = size.y/2;
|
|
3187
3382
|
const colorTopInt = colorTop.rgbaInt();
|
|
3188
3383
|
const colorBottomInt = colorBottom.rgbaInt();
|
|
3189
|
-
const c =
|
|
3384
|
+
const c = cos(-angle), s = sin(-angle);
|
|
3190
3385
|
for (let i=4; i--;)
|
|
3191
3386
|
{
|
|
3192
3387
|
const x = i & 1 ? halfSizeX : -halfSizeX;
|
|
@@ -3237,6 +3432,7 @@ function drawLineList(points, width=.1, color, wrap=false, pos=vec2(), angle=0,
|
|
|
3237
3432
|
ASSERT(!context || !useWebGL, 'context only supported in canvas 2D mode');
|
|
3238
3433
|
if (useWebGL)
|
|
3239
3434
|
{
|
|
3435
|
+
ASSERT(!!glContext, 'WebGL is not enabled!');
|
|
3240
3436
|
let scale = 1;
|
|
3241
3437
|
if (screenSpace)
|
|
3242
3438
|
{
|
|
@@ -3286,6 +3482,8 @@ function drawLine(posA, posB, width=.1, color, pos=vec2(), angle=0, useWebGL, sc
|
|
|
3286
3482
|
const halfDelta = vec2((posB.x - posA.x)/2, (posB.y - posA.y)/2);
|
|
3287
3483
|
const size = vec2(width, halfDelta.length()*2);
|
|
3288
3484
|
pos = pos.add(posA.add(halfDelta));
|
|
3485
|
+
if (screenSpace)
|
|
3486
|
+
halfDelta.y *= -1; // flip angle Y if screen space
|
|
3289
3487
|
angle += halfDelta.angle();
|
|
3290
3488
|
drawRect(pos, size, color, angle, useWebGL, screenSpace, context);
|
|
3291
3489
|
}
|
|
@@ -3313,7 +3511,7 @@ function drawRegularPoly(pos, size=vec2(1), sides=3, color=WHITE, lineWidth=0, l
|
|
|
3313
3511
|
for (let i=sides; i--;)
|
|
3314
3512
|
{
|
|
3315
3513
|
const a = (i/sides)*PI*2;
|
|
3316
|
-
points.push(vec2(
|
|
3514
|
+
points.push(vec2(sin(a)*sizeX, cos(a)*sizeY));
|
|
3317
3515
|
}
|
|
3318
3516
|
drawPoly(points, color, lineWidth, lineColor, pos, angle, useWebGL, screenSpace, context);
|
|
3319
3517
|
}
|
|
@@ -3340,6 +3538,7 @@ function drawPoly(points, color=WHITE, lineWidth=0, lineColor=BLACK, pos=vec2(),
|
|
|
3340
3538
|
|
|
3341
3539
|
if (useWebGL)
|
|
3342
3540
|
{
|
|
3541
|
+
ASSERT(!!glContext, 'WebGL is not enabled!');
|
|
3343
3542
|
let scale = 1;
|
|
3344
3543
|
if (screenSpace)
|
|
3345
3544
|
{
|
|
@@ -3561,18 +3760,18 @@ function drawTextScreen(text, pos, size=1, color=WHITE, lineWidth=0, lineColor=B
|
|
|
3561
3760
|
* @memberof Draw */
|
|
3562
3761
|
function screenToWorld(screenPos)
|
|
3563
3762
|
{
|
|
3564
|
-
let
|
|
3565
|
-
let
|
|
3763
|
+
let x = (screenPos.x - mainCanvasSize.x/2 + .5) / cameraScale;
|
|
3764
|
+
let y = (screenPos.y - mainCanvasSize.y/2 + .5) / -cameraScale;
|
|
3566
3765
|
if (cameraAngle)
|
|
3567
3766
|
{
|
|
3568
3767
|
// apply camera rotation
|
|
3569
|
-
const
|
|
3570
|
-
const rotatedX =
|
|
3571
|
-
const rotatedY =
|
|
3572
|
-
|
|
3573
|
-
|
|
3768
|
+
const c = cos(-cameraAngle), s = sin(-cameraAngle);
|
|
3769
|
+
const rotatedX = x * c - y * s;
|
|
3770
|
+
const rotatedY = x * s + y * c;
|
|
3771
|
+
x = rotatedX;
|
|
3772
|
+
y = rotatedY;
|
|
3574
3773
|
}
|
|
3575
|
-
return new Vector2(
|
|
3774
|
+
return new Vector2(x + cameraPos.x, y + cameraPos.y);
|
|
3576
3775
|
}
|
|
3577
3776
|
|
|
3578
3777
|
/** Convert from world to screen space coordinates
|
|
@@ -3581,24 +3780,64 @@ function screenToWorld(screenPos)
|
|
|
3581
3780
|
* @memberof Draw */
|
|
3582
3781
|
function worldToScreen(worldPos)
|
|
3583
3782
|
{
|
|
3584
|
-
let
|
|
3585
|
-
let
|
|
3783
|
+
let x = worldPos.x - cameraPos.x;
|
|
3784
|
+
let y = worldPos.y - cameraPos.y;
|
|
3586
3785
|
if (cameraAngle)
|
|
3587
3786
|
{
|
|
3588
3787
|
// apply inverse camera rotation
|
|
3589
|
-
const
|
|
3590
|
-
const rotatedX =
|
|
3591
|
-
const rotatedY =
|
|
3592
|
-
|
|
3593
|
-
|
|
3788
|
+
const c = cos(cameraAngle), s = sin(cameraAngle);
|
|
3789
|
+
const rotatedX = x * c - y * s;
|
|
3790
|
+
const rotatedY = x * s + y * c;
|
|
3791
|
+
x = rotatedX;
|
|
3792
|
+
y = rotatedY;
|
|
3594
3793
|
}
|
|
3595
3794
|
return new Vector2
|
|
3596
3795
|
(
|
|
3597
|
-
|
|
3598
|
-
|
|
3796
|
+
x * cameraScale + mainCanvasSize.x/2 - .5,
|
|
3797
|
+
y * -cameraScale + mainCanvasSize.y/2 - .5
|
|
3599
3798
|
);
|
|
3600
3799
|
}
|
|
3601
3800
|
|
|
3801
|
+
/** Convert from screen to world space coordinates for a directional vector (no translation)
|
|
3802
|
+
* @param {Vector2} screenDelta
|
|
3803
|
+
* @return {Vector2}
|
|
3804
|
+
* @memberof Draw */
|
|
3805
|
+
function screenToWorldDelta(screenDelta)
|
|
3806
|
+
{
|
|
3807
|
+
let x = screenDelta.x / cameraScale;
|
|
3808
|
+
let y = screenDelta.y / -cameraScale;
|
|
3809
|
+
if (cameraAngle)
|
|
3810
|
+
{
|
|
3811
|
+
// apply camera rotation
|
|
3812
|
+
const c = cos(-cameraAngle), s = sin(-cameraAngle);
|
|
3813
|
+
const rotatedX = x * c - y * s;
|
|
3814
|
+
const rotatedY = x * s + y * c;
|
|
3815
|
+
x = rotatedX;
|
|
3816
|
+
y = rotatedY;
|
|
3817
|
+
}
|
|
3818
|
+
return new Vector2(x, y);
|
|
3819
|
+
}
|
|
3820
|
+
|
|
3821
|
+
/** Convert from screen to world space coordinates for a directional vector (no translation)
|
|
3822
|
+
* @param {Vector2} worldDelta
|
|
3823
|
+
* @return {Vector2}
|
|
3824
|
+
* @memberof Draw */
|
|
3825
|
+
function worldToScreenDelta(worldDelta)
|
|
3826
|
+
{
|
|
3827
|
+
let x = worldDelta.x;
|
|
3828
|
+
let y = worldDelta.y;
|
|
3829
|
+
if (cameraAngle)
|
|
3830
|
+
{
|
|
3831
|
+
// apply inverse camera rotation
|
|
3832
|
+
const c = cos(cameraAngle), s = sin(cameraAngle);
|
|
3833
|
+
const rotatedX = x * c - y * s;
|
|
3834
|
+
const rotatedY = x * s + y * c;
|
|
3835
|
+
x = rotatedX;
|
|
3836
|
+
y = rotatedY;
|
|
3837
|
+
}
|
|
3838
|
+
return new Vector2(x * cameraScale, y * -cameraScale);
|
|
3839
|
+
}
|
|
3840
|
+
|
|
3602
3841
|
/** Get the camera's visible area in world space
|
|
3603
3842
|
* @return {Vector2}
|
|
3604
3843
|
* @memberof Draw */
|
|
@@ -3651,6 +3890,8 @@ function drawImageColor(context, image, sx, sy, sWidth, sHeight, dx, dy, dWidth,
|
|
|
3651
3890
|
function isBlack(c) { return c.r <= 0 && c.g <= 0 && c.b <= 0 && c.a <= 0; }
|
|
3652
3891
|
const sx2 = bleedScale;
|
|
3653
3892
|
const sy2 = bleedScale;
|
|
3893
|
+
sWidth = max(1,sWidth|0);
|
|
3894
|
+
sHeight = max(1,sHeight|0);
|
|
3654
3895
|
const sWidth2 = sWidth - 2*bleedScale;
|
|
3655
3896
|
const sHeight2 = sHeight - 2*bleedScale;
|
|
3656
3897
|
if (!canvasColorTiles || (additiveColor ? isWhite(color.add(additiveColor)) && additiveColor.a <= 0 : isWhite(color)))
|
|
@@ -3665,7 +3906,7 @@ function drawImageColor(context, image, sx, sy, sWidth, sHeight, dx, dy, dWidth,
|
|
|
3665
3906
|
// copy to offscreen canvas
|
|
3666
3907
|
workCanvas.width = sWidth;
|
|
3667
3908
|
workCanvas.height = sHeight;
|
|
3668
|
-
workContext.drawImage(image, sx, sy, sWidth, sHeight, 0, 0, sWidth, sHeight);
|
|
3909
|
+
workContext.drawImage(image, sx|0, sy|0, sWidth, sHeight, 0, 0, sWidth, sHeight);
|
|
3669
3910
|
|
|
3670
3911
|
// tint image using offscreen work context
|
|
3671
3912
|
const imageData = workContext.getImageData(0, 0, sWidth, sHeight);
|
|
@@ -4002,9 +4243,9 @@ function inputUpdate()
|
|
|
4002
4243
|
if(!(touchInputEnable && isTouchDevice) && !document.hasFocus())
|
|
4003
4244
|
inputClear();
|
|
4004
4245
|
|
|
4005
|
-
// update mouse world space position
|
|
4246
|
+
// update mouse world space position and delta
|
|
4006
4247
|
mousePos = screenToWorld(mousePosScreen);
|
|
4007
|
-
mouseDelta = mouseDeltaScreen
|
|
4248
|
+
mouseDelta = screenToWorldDelta(mouseDeltaScreen);
|
|
4008
4249
|
|
|
4009
4250
|
// update gamepads if enabled
|
|
4010
4251
|
gamepadsUpdate();
|
|
@@ -4036,7 +4277,6 @@ function inputInit()
|
|
|
4036
4277
|
document.addEventListener('wheel', onMouseWheel);
|
|
4037
4278
|
document.addEventListener('contextmenu', onContextMenu);
|
|
4038
4279
|
document.addEventListener('blur', onBlur);
|
|
4039
|
-
document.addEventListener('mouseleave', onMouseLeave);
|
|
4040
4280
|
|
|
4041
4281
|
// init touch input
|
|
4042
4282
|
if (isTouchDevice && touchInputEnable)
|
|
@@ -4078,7 +4318,10 @@ function inputInit()
|
|
|
4078
4318
|
|
|
4079
4319
|
isUsingGamepad = false;
|
|
4080
4320
|
inputData[0][e.button] = 3;
|
|
4321
|
+
|
|
4322
|
+
let mousePosScreenLast = mousePosScreen;
|
|
4081
4323
|
mousePosScreen = mouseEventToScreen(vec2(e.x,e.y));
|
|
4324
|
+
mouseDeltaScreen = mouseDeltaScreen.add(mousePosScreen.subtract(mousePosScreenLast));
|
|
4082
4325
|
inputPreventDefault && e.button && e.preventDefault();
|
|
4083
4326
|
}
|
|
4084
4327
|
function onMouseUp(e)
|
|
@@ -4089,18 +4332,13 @@ function inputInit()
|
|
|
4089
4332
|
}
|
|
4090
4333
|
function onMouseMove(e)
|
|
4091
4334
|
{
|
|
4335
|
+
let mousePosScreenLast = mousePosScreen;
|
|
4092
4336
|
mousePosScreen = mouseEventToScreen(vec2(e.x,e.y));
|
|
4093
|
-
mouseDeltaScreen = mouseDeltaScreen.add(
|
|
4337
|
+
mouseDeltaScreen = mouseDeltaScreen.add(mousePosScreen.subtract(mousePosScreenLast));
|
|
4094
4338
|
}
|
|
4095
4339
|
function onMouseWheel(e) { mouseWheel = e.ctrlKey ? 0 : sign(e.deltaY); }
|
|
4096
4340
|
function onContextMenu(e) { e.preventDefault(); } // prevent right click menu
|
|
4097
4341
|
function onBlur() { inputClear(); } // reset input when focus is lost
|
|
4098
|
-
function onMouseLeave()
|
|
4099
|
-
{
|
|
4100
|
-
// set mouse position and delta when leaving canvas
|
|
4101
|
-
mousePosScreen = vec2(-1);
|
|
4102
|
-
mouseDeltaScreen = vec2(0);
|
|
4103
|
-
}
|
|
4104
4342
|
}
|
|
4105
4343
|
|
|
4106
4344
|
// convert a mouse or touch event position to screen space
|
|
@@ -4144,8 +4382,8 @@ function gamepadsUpdate()
|
|
|
4144
4382
|
else if (touchGamepadStick.lengthSquared() > .3)
|
|
4145
4383
|
{
|
|
4146
4384
|
// convert to 8 way dpad
|
|
4147
|
-
sticks[0].x =
|
|
4148
|
-
sticks[0].y = -
|
|
4385
|
+
sticks[0].x = round(touchGamepadStick.x);
|
|
4386
|
+
sticks[0].y = -round(touchGamepadStick.y);
|
|
4149
4387
|
sticks[0] = sticks[0].clampLength();
|
|
4150
4388
|
}
|
|
4151
4389
|
|
|
@@ -4273,11 +4511,11 @@ function touchInputInit()
|
|
|
4273
4511
|
{
|
|
4274
4512
|
// set event pos and pass it along
|
|
4275
4513
|
const pos = vec2(e.touches[0].clientX, e.touches[0].clientY);
|
|
4276
|
-
const
|
|
4514
|
+
const mousePosScreenLast = mousePosScreen;
|
|
4277
4515
|
mousePosScreen = mouseEventToScreen(pos);
|
|
4278
4516
|
if (wasTouching)
|
|
4279
4517
|
{
|
|
4280
|
-
mouseDeltaScreen = mouseDeltaScreen.add(mousePosScreen.subtract(
|
|
4518
|
+
mouseDeltaScreen = mouseDeltaScreen.add(mousePosScreen.subtract(mousePosScreenLast));
|
|
4281
4519
|
isUsingGamepad = touchGamepadEnable;
|
|
4282
4520
|
}
|
|
4283
4521
|
else
|
|
@@ -4309,7 +4547,7 @@ function touchInputInit()
|
|
|
4309
4547
|
if (touching)
|
|
4310
4548
|
{
|
|
4311
4549
|
touchGamepadTimer.set();
|
|
4312
|
-
if (
|
|
4550
|
+
if (touchGamepadCenterButton && !wasTouching && paused)
|
|
4313
4551
|
{
|
|
4314
4552
|
// touch anywhere to press start when paused
|
|
4315
4553
|
touchGamepadButtons[9] = 1;
|
|
@@ -4348,7 +4586,8 @@ function touchInputInit()
|
|
|
4348
4586
|
if (button < touchGamepadButtonCount)
|
|
4349
4587
|
touchGamepadButtons[button] = 1;
|
|
4350
4588
|
}
|
|
4351
|
-
else if (
|
|
4589
|
+
else if (touchGamepadCenterButton && !wasTouching &&
|
|
4590
|
+
startCenter.distance(touchPos) < touchGamepadSize)
|
|
4352
4591
|
{
|
|
4353
4592
|
// virtual start button in center
|
|
4354
4593
|
touchGamepadButtons[9] = 1;
|
|
@@ -5044,10 +5283,10 @@ function zzfxG
|
|
|
5044
5283
|
|
|
5045
5284
|
// biquad LP/HP filter
|
|
5046
5285
|
quality = 2, w = PI2 * abs(filter) * 2 / sampleRate,
|
|
5047
|
-
|
|
5048
|
-
a0 = 1 + alpha, a1 = -2*
|
|
5049
|
-
b0 = (1 + sign(filter) *
|
|
5050
|
-
b1 = -(sign(filter) +
|
|
5286
|
+
cosw = cos(w), alpha = sin(w) / 2 / quality,
|
|
5287
|
+
a0 = 1 + alpha, a1 = -2*cosw / a0, a2 = (1 - alpha) / a0,
|
|
5288
|
+
b0 = (1 + sign(filter) * cosw) / 2 / a0,
|
|
5289
|
+
b1 = -(sign(filter) + cosw) / a0, b2 = b0,
|
|
5051
5290
|
x2 = 0, x1 = 0, y2 = 0, y1 = 0;
|
|
5052
5291
|
|
|
5053
5292
|
// scale by sample rate
|
|
@@ -5070,15 +5309,15 @@ function zzfxG
|
|
|
5070
5309
|
if (!(++crush%(bitCrush*100|0))) // bit crush
|
|
5071
5310
|
{
|
|
5072
5311
|
s = shape? shape>1? shape>2? shape>3? shape>4? // wave shape
|
|
5073
|
-
(t/PI2%1 < shapeCurve/2? 1 : -1) :
|
|
5074
|
-
|
|
5075
|
-
|
|
5076
|
-
1-(2*t/PI2%2+2)%2:
|
|
5077
|
-
1-4*abs(
|
|
5078
|
-
|
|
5312
|
+
(t/PI2%1 < shapeCurve/2? 1 : -1) : // 5 square duty
|
|
5313
|
+
sin(t**3) : // 4 noise
|
|
5314
|
+
max(min(tan(t),1),-1): // 3 tan
|
|
5315
|
+
1-(2*t/PI2%2+2)%2: // 2 saw
|
|
5316
|
+
1-4*abs(round(t/PI2)-t/PI2): // 1 triangle
|
|
5317
|
+
sin(t); // 0 sin
|
|
5079
5318
|
|
|
5080
5319
|
s = (repeatTime ?
|
|
5081
|
-
1 - tremolo + tremolo*
|
|
5320
|
+
1 - tremolo + tremolo*sin(PI2*i/repeatTime) // tremolo
|
|
5082
5321
|
: 1) *
|
|
5083
5322
|
(shape>4?s:sign(s)*abs(s)**shapeCurve) * // shape curve
|
|
5084
5323
|
(i < attack ? i/attack : // attack
|
|
@@ -5100,8 +5339,8 @@ function zzfxG
|
|
|
5100
5339
|
}
|
|
5101
5340
|
|
|
5102
5341
|
f = (frequency += slide += deltaSlide) *// frequency
|
|
5103
|
-
|
|
5104
|
-
t += f + f*noise*
|
|
5342
|
+
cos(modulation*modOffset++); // modulation
|
|
5343
|
+
t += f + f*noise*sin(i**5); // noise
|
|
5105
5344
|
|
|
5106
5345
|
if (jump && ++jump > pitchJumpTime) // pitch jump
|
|
5107
5346
|
{
|
|
@@ -5167,23 +5406,22 @@ function tileCollisionTest(pos, size=vec2(), object, solidOnly=true)
|
|
|
5167
5406
|
}
|
|
5168
5407
|
}
|
|
5169
5408
|
|
|
5170
|
-
/** Return the
|
|
5171
|
-
* This does not return the exact intersection, but the center of the tile hit.
|
|
5409
|
+
/** Return the exact position of the boudnary of first tile hit, undefined if nothing was hit.
|
|
5172
5410
|
* @param {Vector2} posStart
|
|
5173
5411
|
* @param {Vector2} posEnd
|
|
5174
5412
|
* @param {EngineObject} [object] - An object or undefined for generic test
|
|
5413
|
+
* @param {Vector2} [normal] - Optional normal of the surface hit
|
|
5175
5414
|
* @param {boolean} [solidOnly=true] - Only check solid layers if true
|
|
5176
|
-
* @return {Vector2}
|
|
5415
|
+
* @return {Vector2|undefined} - position of the center of the tile hit or undefined if no hit
|
|
5177
5416
|
* @memberof TileLayers */
|
|
5178
|
-
function tileCollisionRaycast(posStart, posEnd, object, solidOnly=true)
|
|
5417
|
+
function tileCollisionRaycast(posStart, posEnd, object, normal, solidOnly=true)
|
|
5179
5418
|
{
|
|
5180
5419
|
for (const layer of tileCollisionLayers)
|
|
5181
5420
|
{
|
|
5182
5421
|
if (!solidOnly || layer.isSolid)
|
|
5183
5422
|
{
|
|
5184
|
-
const hitPos = layer.collisionRaycast(posStart, posEnd, object)
|
|
5185
|
-
if (hitPos)
|
|
5186
|
-
return hitPos;
|
|
5423
|
+
const hitPos = layer.collisionRaycast(posStart, posEnd, object, normal)
|
|
5424
|
+
if (hitPos) return hitPos;
|
|
5187
5425
|
}
|
|
5188
5426
|
}
|
|
5189
5427
|
}
|
|
@@ -5321,6 +5559,8 @@ class CanvasLayer extends EngineObject
|
|
|
5321
5559
|
/** @property {TextureInfo} - Texture info to use for this object rendering */
|
|
5322
5560
|
const useWebGL = false; // do not use webgl by default
|
|
5323
5561
|
this.textureInfo = new TextureInfo(this.canvas, useWebGL);
|
|
5562
|
+
/** @property {boolean} - True if WebGL texture needs to be refreshed */
|
|
5563
|
+
this.refreshWebGL = false;
|
|
5324
5564
|
|
|
5325
5565
|
// disable physics by default
|
|
5326
5566
|
this.mass = this.gravityScale = this.friction = this.restitution = 0;
|
|
@@ -5354,8 +5594,15 @@ class CanvasLayer extends EngineObject
|
|
|
5354
5594
|
* @memberof Draw */
|
|
5355
5595
|
draw(pos, size, angle=0, color=WHITE, mirror=false, additiveColor, screenSpace=false, context)
|
|
5356
5596
|
{
|
|
5357
|
-
// draw the canvas layer as a single tile that uses the whole texture
|
|
5358
5597
|
const useWebGL = glEnable && this.textureInfo.hasWebGL();
|
|
5598
|
+
if (useWebGL && this.refreshWebGL)
|
|
5599
|
+
{
|
|
5600
|
+
// update the WebGL texture
|
|
5601
|
+
this.textureInfo.createWebGLTexture();
|
|
5602
|
+
this.refreshWebGL = false;
|
|
5603
|
+
}
|
|
5604
|
+
|
|
5605
|
+
// draw the canvas layer as a single tile that uses the whole texture
|
|
5359
5606
|
const tileInfo = new TileInfo().setFullImage(this.textureInfo);
|
|
5360
5607
|
drawTile(pos, size, tileInfo, color, angle, mirror, additiveColor, useWebGL, screenSpace, context);
|
|
5361
5608
|
}
|
|
@@ -5425,9 +5672,18 @@ class CanvasLayer extends EngineObject
|
|
|
5425
5672
|
{ this.drawTile(pos, size, undefined, color, angle); }
|
|
5426
5673
|
|
|
5427
5674
|
/** Create or update the WebGL texture for this layer
|
|
5428
|
-
* @param {boolean} [enable] - enable WebGL rendering and update the texture
|
|
5429
|
-
|
|
5675
|
+
* @param {boolean} [enable] - enable WebGL rendering and update the texture
|
|
5676
|
+
* @param {boolean} [immediate] - shoulkd the texture be updated immediately
|
|
5677
|
+
*/
|
|
5678
|
+
useWebGL(enable=true, immediate=false)
|
|
5430
5679
|
{
|
|
5680
|
+
if (!immediate && enable && this.textureInfo.hasWebGL())
|
|
5681
|
+
{
|
|
5682
|
+
// refresh the texture when needed
|
|
5683
|
+
this.refreshWebGL = true;
|
|
5684
|
+
return;
|
|
5685
|
+
}
|
|
5686
|
+
|
|
5431
5687
|
if (enable)
|
|
5432
5688
|
this.textureInfo.createWebGLTexture();
|
|
5433
5689
|
else
|
|
@@ -5458,7 +5714,7 @@ class TileLayer extends CanvasLayer
|
|
|
5458
5714
|
*/
|
|
5459
5715
|
constructor(position, size, tileInfo=tile(), renderOrder=0)
|
|
5460
5716
|
{
|
|
5461
|
-
const canvasSize = size.multiply(tileInfo.size);
|
|
5717
|
+
const canvasSize = tileInfo ? size.multiply(tileInfo.size) : size;
|
|
5462
5718
|
super(position, size, 0, renderOrder, canvasSize);
|
|
5463
5719
|
|
|
5464
5720
|
// set tile info
|
|
@@ -5511,6 +5767,13 @@ class TileLayer extends CanvasLayer
|
|
|
5511
5767
|
{
|
|
5512
5768
|
ASSERT(drawContext !== this.context, 'must call redrawEnd() after drawing tiles!');
|
|
5513
5769
|
|
|
5770
|
+
if (this.refreshWebGL)
|
|
5771
|
+
{
|
|
5772
|
+
// update the WebGL texture
|
|
5773
|
+
this.textureInfo.createWebGLTexture();
|
|
5774
|
+
this.refreshWebGL = false;
|
|
5775
|
+
}
|
|
5776
|
+
|
|
5514
5777
|
// draw the tile layer as a single tile
|
|
5515
5778
|
const tileInfo = new TileInfo().setFullImage(this.textureInfo);
|
|
5516
5779
|
const size = this.drawSize || this.size;
|
|
@@ -5547,8 +5810,9 @@ class TileLayer extends CanvasLayer
|
|
|
5547
5810
|
drawCanvas = this.canvas;
|
|
5548
5811
|
drawContext = this.context;
|
|
5549
5812
|
cameraPos = this.size.scale(.5);
|
|
5550
|
-
|
|
5551
|
-
|
|
5813
|
+
const tileSize = this.tileInfo ? this.tileInfo.size : vec2(1);
|
|
5814
|
+
cameraScale = tileSize.x;
|
|
5815
|
+
mainCanvasSize = this.size.multiply(tileSize);
|
|
5552
5816
|
if (clear)
|
|
5553
5817
|
{
|
|
5554
5818
|
// clear and set size
|
|
@@ -5708,66 +5972,44 @@ class TileCollisionLayer extends TileLayer
|
|
|
5708
5972
|
// check if the object should collide with this tile
|
|
5709
5973
|
const tileData = this.collisionData[y*this.size.x+x];
|
|
5710
5974
|
if (tileData)
|
|
5711
|
-
if (!object || object.collideWithTile(tileData,
|
|
5975
|
+
if (!object || object.collideWithTile(tileData,
|
|
5976
|
+
hitPos.set(x + this.pos.x, y + this.pos.y)))
|
|
5712
5977
|
return true;
|
|
5713
5978
|
}
|
|
5714
5979
|
return false;
|
|
5715
5980
|
}
|
|
5716
5981
|
|
|
5717
|
-
/** Return the
|
|
5718
|
-
* This does not return the exact intersection, but the center of the tile hit.
|
|
5982
|
+
/** Return the exact position of the boudnary of first tile hit, undefined if nothing was hit.
|
|
5719
5983
|
* @param {Vector2} posStart
|
|
5720
5984
|
* @param {Vector2} posEnd
|
|
5721
|
-
* @param {EngineObject} [object]
|
|
5722
|
-
* @
|
|
5723
|
-
|
|
5985
|
+
* @param {EngineObject} [object] - An object or undefined for generic test
|
|
5986
|
+
* @param {Vector2} [normal] - Optional normal of the surface hit
|
|
5987
|
+
* @return {Vector2|undefined} */
|
|
5988
|
+
collisionRaycast(posStart, posEnd, object, normal)
|
|
5724
5989
|
{
|
|
5725
5990
|
ASSERT(isVector2(posStart) && isVector2(posEnd), 'positions must be Vector2s');
|
|
5726
5991
|
ASSERT(!object || object instanceof EngineObject, 'object must be an EngineObject');
|
|
5727
|
-
|
|
5728
|
-
|
|
5729
|
-
const
|
|
5730
|
-
const posStartY = posStart.y - this.pos.y;
|
|
5731
|
-
const posEndX = posEnd.x - this.pos.x;
|
|
5732
|
-
const posEndY = posEnd.y - this.pos.y;
|
|
5733
|
-
|
|
5734
|
-
// test if a ray collides with tiles from start to end
|
|
5735
|
-
const deltaX = posEndX - posStartX;
|
|
5736
|
-
const deltaY = posEndY - posStartY;
|
|
5737
|
-
const totalLength = (deltaX**2 + deltaY**2)**.5;
|
|
5738
|
-
const unitX = abs(totalLength/deltaX);
|
|
5739
|
-
const unitY = abs(totalLength/deltaY);
|
|
5740
|
-
|
|
5741
|
-
// setup iteration variables
|
|
5742
|
-
const pos = posStart.floor(), signDeltaX = sign(deltaX), signDeltaY = sign(deltaY);
|
|
5743
|
-
let xi = unitX * (deltaX < 0 ? posStart.x - pos.x : pos.x - posStart.x + 1) || 0;
|
|
5744
|
-
let yi = unitY * (deltaY < 0 ? posStart.y - pos.y : pos.y - posStart.y + 1) || 0;
|
|
5745
|
-
|
|
5746
|
-
// use line drawing algorithm to test for collisions
|
|
5747
|
-
while (true)
|
|
5992
|
+
|
|
5993
|
+
const localPos = new Vector2;
|
|
5994
|
+
const collisionTest = (pos)=>
|
|
5748
5995
|
{
|
|
5749
5996
|
// check for tile collision
|
|
5750
|
-
|
|
5751
|
-
|
|
5752
|
-
|
|
5753
|
-
pos.x += .5; pos.y += .5;
|
|
5754
|
-
debugRaycast && debugLine(posStart, posEnd, '#f00', .02);
|
|
5755
|
-
debugRaycast && debugPoint(pos, '#ff0');
|
|
5756
|
-
return pos;
|
|
5757
|
-
}
|
|
5758
|
-
|
|
5759
|
-
// check if past the end
|
|
5760
|
-
if (xi >= totalLength && yi >= totalLength)
|
|
5761
|
-
break;
|
|
5762
|
-
|
|
5763
|
-
// get coordinates of next tile to check
|
|
5764
|
-
if (xi > yi)
|
|
5765
|
-
pos.y += signDeltaY, yi += unitY;
|
|
5766
|
-
else
|
|
5767
|
-
pos.x += signDeltaX, xi += unitX;
|
|
5997
|
+
localPos.set(pos.x - this.pos.x, pos.y - this.pos.y);
|
|
5998
|
+
const tileData = this.getCollisionData(localPos);
|
|
5999
|
+
return tileData && (!object || object.collideWithTile(tileData, pos));
|
|
5768
6000
|
}
|
|
5769
|
-
|
|
5770
6001
|
debugRaycast && debugLine(posStart, posEnd, '#00f', .02);
|
|
6002
|
+
const hitPos = lineTest(posStart, posEnd, collisionTest, normal);
|
|
6003
|
+
if (hitPos)
|
|
6004
|
+
{
|
|
6005
|
+
const tilePos = hitPos.floor().add(vec2(.5));
|
|
6006
|
+
debugRaycast && debugRect(tilePos, vec2(1), '#f008');
|
|
6007
|
+
debugRaycast && debugLine(posStart, hitPos, '#f00', .02);
|
|
6008
|
+
debugRaycast && debugPoint(hitPos, '#0f0');
|
|
6009
|
+
debugRaycast && normal &&
|
|
6010
|
+
debugLine(hitPos, hitPos.add(normal), '#ff0', .02);
|
|
6011
|
+
return hitPos;
|
|
6012
|
+
}
|
|
5771
6013
|
}
|
|
5772
6014
|
}
|
|
5773
6015
|
/**
|
|
@@ -5922,6 +6164,12 @@ class ParticleEmitter extends EngineObject
|
|
|
5922
6164
|
this.particleCreateCallback = undefined;
|
|
5923
6165
|
/** @property {number} - Track particle emit time */
|
|
5924
6166
|
this.emitTimeBuffer = 0;
|
|
6167
|
+
/** @property {number} - Percentage of velocity to pass to particles (0-1) */
|
|
6168
|
+
this.velocityInheritance = 0;
|
|
6169
|
+
|
|
6170
|
+
// track previous position and angle
|
|
6171
|
+
this.previousAngle = this.angle;
|
|
6172
|
+
this.previousPos = this.pos.copy();
|
|
5925
6173
|
}
|
|
5926
6174
|
|
|
5927
6175
|
/** Update the emitter to spawn particles, called automatically by engine once each frame */
|
|
@@ -5930,6 +6178,18 @@ class ParticleEmitter extends EngineObject
|
|
|
5930
6178
|
// only do default update to apply parent transforms
|
|
5931
6179
|
this.parent && super.update();
|
|
5932
6180
|
|
|
6181
|
+
if (this.velocityInheritance)
|
|
6182
|
+
{
|
|
6183
|
+
// pass emitter velocity to particles
|
|
6184
|
+
const p = this.velocityInheritance;
|
|
6185
|
+
this.velocity.x = p * (this.pos.x - this.previousPos.x);
|
|
6186
|
+
this.velocity.y = p * (this.pos.y - this.previousPos.y);
|
|
6187
|
+
this.angleVelocity = p * (this.angle - this.previousAngle);
|
|
6188
|
+
this.previousAngle = this.angle;
|
|
6189
|
+
this.previousPos.x = this.pos.x;
|
|
6190
|
+
this.previousPos.y = this.pos.y;
|
|
6191
|
+
}
|
|
6192
|
+
|
|
5933
6193
|
// update emitter
|
|
5934
6194
|
if (!this.emitTime || this.getAliveTime() <= this.emitTime)
|
|
5935
6195
|
{
|
|
@@ -5987,7 +6247,7 @@ class ParticleEmitter extends EngineObject
|
|
|
5987
6247
|
const particle = new Particle(pos, this.tileInfo, angle, colorStart, colorEnd, particleTime, sizeStart, sizeEnd, this.fadeRate, this.additive, this.trailScale, this.localSpace && this, this.particleDestroyCallback);
|
|
5988
6248
|
particle.velocity = vec2().setAngle(velocityAngle, speed);
|
|
5989
6249
|
particle.angleVelocity = angleSpeed;
|
|
5990
|
-
if (!this.localSpace)
|
|
6250
|
+
if (!this.localSpace && this.velocityInheritance > 0)
|
|
5991
6251
|
{
|
|
5992
6252
|
// apply emitter velocity to particle
|
|
5993
6253
|
particle.velocity.x += this.velocity.x;
|
|
@@ -6085,6 +6345,16 @@ class Particle extends EngineObject
|
|
|
6085
6345
|
this.velocity.y *= s;
|
|
6086
6346
|
}
|
|
6087
6347
|
}
|
|
6348
|
+
|
|
6349
|
+
if (this.lifeTime > 0 && time - this.spawnTime > this.lifeTime)
|
|
6350
|
+
{
|
|
6351
|
+
// destroy particle when its time runs out
|
|
6352
|
+
const c = this.colorEnd;
|
|
6353
|
+
this.color.set(c.r, c.g, c.b, c.a);
|
|
6354
|
+
this.size.set(this.sizeEnd, this.sizeEnd);
|
|
6355
|
+
this.destroyCallback && this.destroyCallback(this);
|
|
6356
|
+
this.destroyed = 1;
|
|
6357
|
+
}
|
|
6088
6358
|
}
|
|
6089
6359
|
|
|
6090
6360
|
/** Render the particle, automatically called each frame, sorted by renderOrder */
|
|
@@ -6093,7 +6363,7 @@ class Particle extends EngineObject
|
|
|
6093
6363
|
// lerp color and size
|
|
6094
6364
|
const p1 = this.lifeTime > 0 ? min((time - this.spawnTime) / this.lifeTime, 1) : 1, p2 = 1-p1;
|
|
6095
6365
|
const radius = p2 * this.sizeStart + p1 * this.sizeEnd;
|
|
6096
|
-
|
|
6366
|
+
const size = vec2(radius);
|
|
6097
6367
|
this.color.r = p2 * this.colorStart.r + p1 * this.colorEnd.r;
|
|
6098
6368
|
this.color.g = p2 * this.colorStart.g + p1 * this.colorEnd.g;
|
|
6099
6369
|
this.color.b = p2 * this.colorStart.b + p1 * this.colorEnd.b;
|
|
@@ -6113,7 +6383,7 @@ class Particle extends EngineObject
|
|
|
6113
6383
|
{
|
|
6114
6384
|
// in local space of emitter
|
|
6115
6385
|
const a = this.localSpaceEmitter.angle;
|
|
6116
|
-
const c =
|
|
6386
|
+
const c = cos(a), s = sin(a);
|
|
6117
6387
|
pos = this.localSpaceEmitter.pos.add(
|
|
6118
6388
|
new Vector2(pos.x*c - pos.y*s, pos.x*s + pos.y*c));
|
|
6119
6389
|
angle += this.localSpaceEmitter.angle;
|
|
@@ -6129,22 +6399,15 @@ class Particle extends EngineObject
|
|
|
6129
6399
|
{
|
|
6130
6400
|
// stretch in direction of motion
|
|
6131
6401
|
const trailLength = speed * this.trailScale;
|
|
6132
|
-
|
|
6133
|
-
angle =
|
|
6134
|
-
drawTile(pos,
|
|
6402
|
+
size.y = max(size.x, trailLength);
|
|
6403
|
+
angle = atan2(direction.x, direction.y);
|
|
6404
|
+
drawTile(pos, size, this.tileInfo, this.color, angle, this.mirror);
|
|
6135
6405
|
}
|
|
6136
6406
|
}
|
|
6137
6407
|
else
|
|
6138
|
-
drawTile(pos,
|
|
6408
|
+
drawTile(pos, size, this.tileInfo, this.color, angle, this.mirror);
|
|
6139
6409
|
this.additive && setBlendMode();
|
|
6140
|
-
debugParticles && debugRect(pos,
|
|
6141
|
-
|
|
6142
|
-
if (p1 === 1)
|
|
6143
|
-
{
|
|
6144
|
-
// destroy particle when its time runs out
|
|
6145
|
-
this.destroyCallback && this.destroyCallback(this);
|
|
6146
|
-
this.destroyed = 1;
|
|
6147
|
-
}
|
|
6410
|
+
debugParticles && debugRect(pos, size, '#f005', 0, angle);
|
|
6148
6411
|
}
|
|
6149
6412
|
}
|
|
6150
6413
|
/**
|
|
@@ -6367,7 +6630,7 @@ let glContext;
|
|
|
6367
6630
|
let glAntialias = true;
|
|
6368
6631
|
|
|
6369
6632
|
// WebGL internal variables not exposed to documentation
|
|
6370
|
-
let glShader, glPolyShader, glPolyMode, glAdditive, glBatchAdditive, glActiveTexture, glArrayBuffer, glGeometryBuffer, glPositionData, glColorData, glBatchCount, glTextureInfos;
|
|
6633
|
+
let glShader, glPolyShader, glPolyMode, glAdditive, glBatchAdditive, glActiveTexture, glArrayBuffer, glGeometryBuffer, glPositionData, glColorData, glBatchCount, glTextureInfos, glCanBeEnabled = true;
|
|
6371
6634
|
|
|
6372
6635
|
// WebGL internal constants
|
|
6373
6636
|
const gl_ARRAY_BUFFER_SIZE = 5e5;
|
|
@@ -6386,7 +6649,11 @@ function glInit()
|
|
|
6386
6649
|
// keep set of texture infos so they can be restored if context is lost
|
|
6387
6650
|
glTextureInfos = new Set;
|
|
6388
6651
|
|
|
6389
|
-
if (!glEnable || headlessMode)
|
|
6652
|
+
if (!glEnable || headlessMode)
|
|
6653
|
+
{
|
|
6654
|
+
glCanBeEnabled = false;
|
|
6655
|
+
return;
|
|
6656
|
+
}
|
|
6390
6657
|
|
|
6391
6658
|
// create the canvas and textures
|
|
6392
6659
|
glCanvas = document.createElement('canvas');
|
|
@@ -6397,10 +6664,11 @@ function glInit()
|
|
|
6397
6664
|
console.warn('WebGL2 not supported, falling back to 2D canvas rendering!');
|
|
6398
6665
|
glCanvas = glContext = undefined;
|
|
6399
6666
|
glEnable = false;
|
|
6667
|
+
glCanBeEnabled = false;
|
|
6400
6668
|
return;
|
|
6401
6669
|
}
|
|
6402
6670
|
|
|
6403
|
-
//
|
|
6671
|
+
// attach the WebGL canvas
|
|
6404
6672
|
const rootElement = mainCanvas.parentElement;
|
|
6405
6673
|
rootElement.appendChild(glCanvas);
|
|
6406
6674
|
|
|
@@ -6423,7 +6691,7 @@ function glInit()
|
|
|
6423
6691
|
});
|
|
6424
6692
|
glCanvas.addEventListener('webglcontextrestored', ()=>
|
|
6425
6693
|
{
|
|
6426
|
-
glEnable = true; //
|
|
6694
|
+
glEnable = true; // re-enable WebGL rendering
|
|
6427
6695
|
glCanvas.style.display = ''; // show the gl canvas
|
|
6428
6696
|
LOG('WebGL context restored, reinitializing...');
|
|
6429
6697
|
|
|
@@ -6575,8 +6843,8 @@ function glPreRender()
|
|
|
6575
6843
|
const s = vec2(2*cameraScale).divide(mainCanvasSize);
|
|
6576
6844
|
const rotatedCam = cameraPos.rotate(-cameraAngle);
|
|
6577
6845
|
const p = vec2(-1).subtract(rotatedCam.multiply(s));
|
|
6578
|
-
const ca =
|
|
6579
|
-
const sa =
|
|
6846
|
+
const ca = cos(cameraAngle);
|
|
6847
|
+
const sa = sin(cameraAngle);
|
|
6580
6848
|
const transform = [
|
|
6581
6849
|
s.x * ca, s.y * sa, 0, 0,
|
|
6582
6850
|
-s.x * sa, s.y * ca, 0, 0,
|
|
@@ -6881,8 +7149,8 @@ function glDrawPointsTransform(points, rgba, x, y, sx, sy, angle, tristrip=true)
|
|
|
6881
7149
|
// transform the point
|
|
6882
7150
|
const px = p.x*sx;
|
|
6883
7151
|
const py = p.y*sy;
|
|
6884
|
-
const sa =
|
|
6885
|
-
const ca =
|
|
7152
|
+
const sa = sin(-angle);
|
|
7153
|
+
const ca = cos(-angle);
|
|
6886
7154
|
pointsOut.push(vec2(x + ca*px - sa*py, y + sa*px + ca*py));
|
|
6887
7155
|
}
|
|
6888
7156
|
const drawPoints = tristrip ? glPolyStrip(pointsOut) : pointsOut;
|