littlejsengine 1.7.13 → 1.8.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 +27 -55
- package/build/littlejs.d.ts +253 -76
- package/build/littlejs.esm.js +644 -523
- package/build/littlejs.esm.min.js +1 -1
- package/build/littlejs.js +640 -332
- package/build/littlejs.min.js +1 -1
- package/build/littlejs.release.js +640 -332
- package/examples/breakout/game.js +17 -14
- package/examples/breakout/gameObjects.js +29 -8
- package/examples/breakout/index.html +3 -3
- package/examples/breakoutTutorial/README.md +2 -2
- package/examples/breakoutTutorial/game.js +4 -4
- package/examples/breakoutTutorial/index.html +2 -2
- package/examples/electron/build.js +2 -0
- package/examples/electron/game.js +3 -2
- package/examples/electron/index.html +2 -2
- package/examples/empty/game.js +1 -1
- package/examples/favicon.png +0 -0
- package/examples/js13k/build.js +2 -0
- package/examples/js13k/game.js +20 -15
- package/examples/js13k/index.html +14 -14
- package/examples/module/game.js +4 -3
- package/examples/module/index.html +1 -1
- package/examples/particles/index.html +19 -22
- package/examples/platformer/game.js +3 -3
- package/examples/platformer/gameEffects.js +21 -19
- package/examples/platformer/gameLevel.js +6 -6
- package/examples/platformer/gameObjects.js +18 -18
- package/examples/platformer/gamePlayer.js +4 -3
- package/examples/platformer/index.html +6 -6
- package/examples/platformer/tiles.png +0 -0
- package/examples/platformer/tilesLevel.png +0 -0
- package/examples/puzzle/game.js +10 -8
- package/examples/puzzle/index.html +2 -2
- package/examples/screenshot.jpg +0 -0
- package/examples/starter/build.js +2 -0
- package/examples/starter/game.js +32 -21
- package/examples/starter/index.html +13 -13
- package/examples/starter/tiles.png +0 -0
- package/examples/stress/index.html +2 -2
- package/examples/typescript/build.js +2 -0
- package/examples/typescript/game.js +4 -3
- package/examples/typescript/game.ts +4 -3
- package/examples/typescript/index.html +1 -1
- package/package.json +1 -1
- package/src/engine.js +59 -49
- package/src/engineAudio.js +67 -41
- package/src/engineBuild.js +23 -7
- package/src/engineDraw.js +139 -40
- package/src/engineExport.js +4 -191
- package/src/engineInput.js +22 -10
- package/src/engineMedals.js +13 -45
- package/src/engineObject.js +12 -13
- package/src/engineParticles.js +17 -17
- package/src/engineSettings.js +195 -2
- package/src/engineTileLayer.js +23 -22
- package/src/engineUtilities.js +6 -6
- package/src/engineWebGL.js +73 -74
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
// LittleJS - MIT License - Copyright 2021 Frank Force
|
|
2
2
|
|
|
3
|
+
'use strict';
|
|
4
|
+
|
|
3
5
|
/**
|
|
4
6
|
* LittleJS - Release Mode
|
|
5
7
|
* - This file is used for release builds in place of engineDebug.js
|
|
6
8
|
* - Debug functionality is disabled to reduce size and increase performance
|
|
7
9
|
*/
|
|
8
10
|
|
|
9
|
-
|
|
11
|
+
|
|
10
12
|
|
|
11
13
|
let showWatermark = 0;
|
|
12
14
|
let debugKeyCode = 0;
|
|
@@ -41,7 +43,7 @@ function debugSaveCanvas (){}
|
|
|
41
43
|
* @namespace Utilities
|
|
42
44
|
*/
|
|
43
45
|
|
|
44
|
-
|
|
46
|
+
|
|
45
47
|
|
|
46
48
|
/** A shortcut to get Math.PI
|
|
47
49
|
* @type {Number}
|
|
@@ -238,10 +240,10 @@ function randColor(colorA=new Color, colorB=new Color(0,0,0,1), linear)
|
|
|
238
240
|
* - Can be used to create a deterministic random number sequence
|
|
239
241
|
* @example
|
|
240
242
|
* let r = new RandomGenerator(123); // random number generator with seed 123
|
|
241
|
-
* let a = r.
|
|
242
|
-
* let b = r.
|
|
243
|
+
* let a = r.float(); // random value between 0 and 1
|
|
244
|
+
* let b = r.int(10); // random integer between 0 and 9
|
|
243
245
|
* r.seed = 123; // reset the seed
|
|
244
|
-
* let c = r.
|
|
246
|
+
* let c = r.float(); // the same value as a
|
|
245
247
|
*/
|
|
246
248
|
class RandomGenerator
|
|
247
249
|
{
|
|
@@ -270,18 +272,18 @@ class RandomGenerator
|
|
|
270
272
|
* @param {Number} valueA
|
|
271
273
|
* @param {Number} [valueB=0]
|
|
272
274
|
* @return {Number} */
|
|
273
|
-
int(valueA, valueB=0) { return Math.floor(this.
|
|
275
|
+
int(valueA, valueB=0) { return Math.floor(this.float(valueA, valueB)); }
|
|
274
276
|
|
|
275
277
|
/** Randomly returns either -1 or 1 deterministically
|
|
276
278
|
* @return {Number} */
|
|
277
|
-
sign() { return this.
|
|
279
|
+
sign() { return this.int(2) * 2 - 1; }
|
|
278
280
|
}
|
|
279
281
|
|
|
280
282
|
///////////////////////////////////////////////////////////////////////////////
|
|
281
283
|
|
|
282
284
|
/**
|
|
283
285
|
* Create a 2d vector, can take another Vector2 to copy, 2 scalars, or 1 scalar
|
|
284
|
-
* @param {Number} [x=0]
|
|
286
|
+
* @param {(Number|Vector2)} [x=0]
|
|
285
287
|
* @param {Number} [y=0]
|
|
286
288
|
* @return {Vector2}
|
|
287
289
|
* @example
|
|
@@ -696,7 +698,7 @@ class Timer
|
|
|
696
698
|
* @namespace Settings
|
|
697
699
|
*/
|
|
698
700
|
|
|
699
|
-
|
|
701
|
+
|
|
700
702
|
|
|
701
703
|
///////////////////////////////////////////////////////////////////////////////
|
|
702
704
|
// Camera settings
|
|
@@ -765,7 +767,7 @@ let glOverlay = 1;
|
|
|
765
767
|
* @memberof Settings */
|
|
766
768
|
let tileSizeDefault = vec2(16);
|
|
767
769
|
|
|
768
|
-
/**
|
|
770
|
+
/** How many pixels smaller to draw tiles to prevent bleeding from neighbors
|
|
769
771
|
* @type {Number}
|
|
770
772
|
* @default
|
|
771
773
|
* @memberof Settings */
|
|
@@ -939,12 +941,205 @@ let medalDisplayIconSize = 50;
|
|
|
939
941
|
* @type {Boolean}
|
|
940
942
|
* @default 0
|
|
941
943
|
* @memberof Settings */
|
|
942
|
-
let medalsPreventUnlock;
|
|
944
|
+
let medalsPreventUnlock;
|
|
945
|
+
|
|
946
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
947
|
+
// Setters for global variables
|
|
948
|
+
|
|
949
|
+
/** Set position of camera in world space
|
|
950
|
+
* @param {Vector2} pos
|
|
951
|
+
* @memberof Settings */
|
|
952
|
+
function setCameraPos(pos) { cameraPos = pos; }
|
|
953
|
+
|
|
954
|
+
/** Set scale of camera in world space
|
|
955
|
+
* @param {Number} scale
|
|
956
|
+
* @memberof Settings */
|
|
957
|
+
function setCameraScale(scale) { cameraScale = scale; }
|
|
958
|
+
|
|
959
|
+
/** Set max size of the canvas
|
|
960
|
+
* @param {Vector2} size
|
|
961
|
+
* @memberof Settings */
|
|
962
|
+
function setCanvasMaxSize(size) { canvasMaxSize = size; }
|
|
963
|
+
|
|
964
|
+
/** Set fixed size of the canvas
|
|
965
|
+
* @param {Vector2} size
|
|
966
|
+
* @memberof Settings */
|
|
967
|
+
function setCanvasFixedSize(size) { canvasFixedSize = size; }
|
|
968
|
+
|
|
969
|
+
/** Disables anti aliasing for pixel art if true
|
|
970
|
+
* @param {Boolean} pixelated
|
|
971
|
+
* @memberof Settings */
|
|
972
|
+
function setCanvasPixelated(pixelated) { canvasPixelated = pixelated; }
|
|
973
|
+
|
|
974
|
+
/** Set default font used for text rendering
|
|
975
|
+
* @param {String} font
|
|
976
|
+
* @memberof Settings */
|
|
977
|
+
function setFontDefault(font) { fontDefault = font; }
|
|
978
|
+
|
|
979
|
+
/** Set if webgl rendering is enabled
|
|
980
|
+
* @param {Boolean} enable
|
|
981
|
+
* @memberof Settings */
|
|
982
|
+
function setGlEnable(enable) { glEnable = enable; }
|
|
983
|
+
|
|
984
|
+
/** Set to not composite the WebGL canvas
|
|
985
|
+
* @param {Boolean} overlay
|
|
986
|
+
* @memberof Settings */
|
|
987
|
+
function setGlOverlay(overlay) { glOverlay = overlay; }
|
|
988
|
+
|
|
989
|
+
/** Set default size of tiles in pixels
|
|
990
|
+
* @param {Vector2} size
|
|
991
|
+
* @memberof Settings */
|
|
992
|
+
function setTileSizeDefault(size) { tileSizeDefault = size; }
|
|
993
|
+
|
|
994
|
+
/** Set to prevent tile bleeding from neighbors in pixels
|
|
995
|
+
* @param {Number} scale
|
|
996
|
+
* @memberof Settings */
|
|
997
|
+
function setTileFixBleedScale(scale) { tileFixBleedScale = scale; }
|
|
998
|
+
|
|
999
|
+
/** Set if collisions between objects are enabled
|
|
1000
|
+
* @param {Boolean} enable
|
|
1001
|
+
* @memberof Settings */
|
|
1002
|
+
function setEnablePhysicsSolver(enable) { enablePhysicsSolver = enable; }
|
|
1003
|
+
|
|
1004
|
+
/** Set default object mass for collison calcuations
|
|
1005
|
+
* @param {Number} mass
|
|
1006
|
+
* @memberof Settings */
|
|
1007
|
+
function setObjectDefaultMass(mass) { objectDefaultMass = mass; }
|
|
1008
|
+
|
|
1009
|
+
/** Set how much to slow velocity by each frame
|
|
1010
|
+
* @param {Number} damping
|
|
1011
|
+
* @memberof Settings */
|
|
1012
|
+
function setObjectDefaultDamping(damp) { objectDefaultDamping = damp; }
|
|
1013
|
+
|
|
1014
|
+
/** Set how much to slow angular velocity each frame
|
|
1015
|
+
* @param {Number} damping
|
|
1016
|
+
* @memberof Settings */
|
|
1017
|
+
function setObjectDefaultAngleDamping(damp) { objectDefaultAngleDamping = damp; }
|
|
1018
|
+
|
|
1019
|
+
/** Set how much to bounce when a collision occur
|
|
1020
|
+
* @param {Number} elasticity
|
|
1021
|
+
* @memberof Settings */
|
|
1022
|
+
function setObjectDefaultElasticity(elasticity) { objectDefaultElasticity = elasticity; }
|
|
1023
|
+
|
|
1024
|
+
/** Set how much to slow when touching
|
|
1025
|
+
* @param {Number} friction
|
|
1026
|
+
* @memberof Settings */
|
|
1027
|
+
function setObjectDefaultFriction(friction) { objectDefaultFriction = friction; }
|
|
1028
|
+
|
|
1029
|
+
/** Set max speed to avoid fast objects missing collisions
|
|
1030
|
+
* @param {Number} speed
|
|
1031
|
+
* @memberof Settings */
|
|
1032
|
+
function setObjectMaxSpeed(speed) { objectMaxSpeed = speed; }
|
|
1033
|
+
|
|
1034
|
+
/** Set how much gravity to apply to objects along the Y axis
|
|
1035
|
+
* @param {Number} gravity
|
|
1036
|
+
* @memberof Settings */
|
|
1037
|
+
function setGravity(g) { gravity = g; }
|
|
1038
|
+
|
|
1039
|
+
/** Set to scales emit rate of particles
|
|
1040
|
+
* @param {Number} scale
|
|
1041
|
+
* @memberof Settings */
|
|
1042
|
+
function setParticleEmitRateScale(scale) { particleEmitRateScale = scale; }
|
|
1043
|
+
|
|
1044
|
+
/** Set if gamepads are enabled
|
|
1045
|
+
* @param {Boolean} enable
|
|
1046
|
+
* @memberof Settings */
|
|
1047
|
+
function setGamepadsEnable(enable) { gamepadsEnable = enable; }
|
|
1048
|
+
|
|
1049
|
+
/** Set if the dpad input is also routed to the left analog stick
|
|
1050
|
+
* @param {Boolean} enable
|
|
1051
|
+
* @memberof Settings */
|
|
1052
|
+
function setGamepadDirectionEmulateStick(enable) { gamepadDirectionEmulateStick = enable; }
|
|
1053
|
+
|
|
1054
|
+
/** Set if true the WASD keys are also routed to the direction keys
|
|
1055
|
+
* @param {Boolean} enable
|
|
1056
|
+
* @memberof Settings */
|
|
1057
|
+
function setInputWASDEmulateDirection(enable) { inputWASDEmulateDirection = enable; }
|
|
1058
|
+
|
|
1059
|
+
/** Set if touch gamepad should appear on mobile devices
|
|
1060
|
+
* @param {Boolean} enable
|
|
1061
|
+
* @memberof Settings */
|
|
1062
|
+
function setTouchGamepadEnable(enable) { touchGamepadEnable = enable; }
|
|
1063
|
+
|
|
1064
|
+
/** Set if touch gamepad should be analog stick or 8 way dpad
|
|
1065
|
+
* @param {Boolean} analog
|
|
1066
|
+
* @memberof Settings */
|
|
1067
|
+
function setTouchGamepadAnalog(analog) { touchGamepadAnalog = analog; }
|
|
1068
|
+
|
|
1069
|
+
/** Set size of virutal gamepad for touch devices in pixels
|
|
1070
|
+
* @param {Number} size
|
|
1071
|
+
* @memberof Settings */
|
|
1072
|
+
function setTouchGamepadSize(size) { touchGamepadSize = size; }
|
|
1073
|
+
|
|
1074
|
+
/** Set transparency of touch gamepad overlay
|
|
1075
|
+
* @param {Number} alpha
|
|
1076
|
+
* @memberof Settings */
|
|
1077
|
+
function setTouchGamepadAlpha(alpha) { touchGamepadAlpha = alpha; }
|
|
1078
|
+
|
|
1079
|
+
/** Set to allow vibration hardware if it exists
|
|
1080
|
+
* @param {Boolean} enable
|
|
1081
|
+
* @memberof Settings */
|
|
1082
|
+
function setVibrateEnable(enable) { vibrateEnable = enable; }
|
|
1083
|
+
|
|
1084
|
+
/** Set to disable all audio code
|
|
1085
|
+
* @param {Boolean} enable
|
|
1086
|
+
* @memberof Settings */
|
|
1087
|
+
function setSoundEnable(enable) { soundEnable = enable; }
|
|
1088
|
+
|
|
1089
|
+
/** Set volume scale to apply to all sound, music and speech
|
|
1090
|
+
* @param {Number} volume
|
|
1091
|
+
* @memberof Settings */
|
|
1092
|
+
function setSoundVolume(volume) { soundVolume = volume; }
|
|
1093
|
+
|
|
1094
|
+
/** Set default range where sound no longer plays
|
|
1095
|
+
* @param {Number} range
|
|
1096
|
+
* @memberof Settings */
|
|
1097
|
+
function setSoundDefaultRange(range) { soundDefaultRange = range; }
|
|
1098
|
+
|
|
1099
|
+
/** Set default range percent to start tapering off sound
|
|
1100
|
+
* @param {Number} taper
|
|
1101
|
+
* @memberof Settings */
|
|
1102
|
+
function setSoundDefaultTaper(taper) { soundDefaultTaper = taper; }
|
|
1103
|
+
|
|
1104
|
+
/** Set how long to show medals for in seconds
|
|
1105
|
+
* @param {Number} time
|
|
1106
|
+
* @memberof Settings */
|
|
1107
|
+
function setMedalDisplayTime(time) { medalDisplayTime = time; }
|
|
1108
|
+
|
|
1109
|
+
/** Set how quickly to slide on/off medals in seconds
|
|
1110
|
+
* @param {Number} time
|
|
1111
|
+
* @memberof Settings */
|
|
1112
|
+
function setMedalDisplaySlideTime(time) { medalDisplaySlideTime = time; }
|
|
1113
|
+
|
|
1114
|
+
/** Set size of medal display
|
|
1115
|
+
* @param {Vector2} size
|
|
1116
|
+
* @memberof Settings */
|
|
1117
|
+
function setMedalDisplaySize(size) { medalDisplaySize = size; }
|
|
1118
|
+
|
|
1119
|
+
/** Set size of icon in medal display
|
|
1120
|
+
* @param {Number} size
|
|
1121
|
+
* @memberof Settings */
|
|
1122
|
+
function setMedalDisplayIconSize(size) { medalDisplayIconSize = size; }
|
|
1123
|
+
|
|
1124
|
+
/** Set to stop medals from being unlockable
|
|
1125
|
+
* @param {Boolean} preventUnlock
|
|
1126
|
+
* @memberof Settings */
|
|
1127
|
+
function setMedalsPreventUnlock(preventUnlock) { medalsPreventUnlock = preventUnlock; }
|
|
1128
|
+
|
|
1129
|
+
/** Set if watermark with FPS should be shown
|
|
1130
|
+
* @param {Boolean} show
|
|
1131
|
+
* @memberof Debug */
|
|
1132
|
+
function setShowWatermark(show) { showWatermark = show; }
|
|
1133
|
+
|
|
1134
|
+
/** Set key code used to toggle debug mode, Esc by default
|
|
1135
|
+
* @param {Number} key
|
|
1136
|
+
* @memberof Debug */
|
|
1137
|
+
function setDebugKey(key) { debugKey = key; }
|
|
943
1138
|
/**
|
|
944
1139
|
* LittleJS Object System
|
|
945
1140
|
*/
|
|
946
1141
|
|
|
947
|
-
|
|
1142
|
+
|
|
948
1143
|
|
|
949
1144
|
/**
|
|
950
1145
|
* LittleJS Object Base Object Class
|
|
@@ -974,18 +1169,19 @@ let medalsPreventUnlock;
|
|
|
974
1169
|
class EngineObject
|
|
975
1170
|
{
|
|
976
1171
|
/** Create an engine object and adds it to the list of objects
|
|
977
|
-
* @param {Vector2} [
|
|
978
|
-
* @param {Vector2} [size=Vector2(1,1)]
|
|
979
|
-
* @param {
|
|
980
|
-
* @param {
|
|
981
|
-
* @param {
|
|
982
|
-
* @param {
|
|
983
|
-
* @param {Number} [renderOrder=0] - Objects sorted by renderOrder before being rendered
|
|
1172
|
+
* @param {Vector2} [pos=Vector2()] - World space position of the object
|
|
1173
|
+
* @param {Vector2} [size=Vector2(1,1)] - World space size of the object
|
|
1174
|
+
* @param {TileInfo} [tileInfo] - Tile info to render object (undefined is untextured)
|
|
1175
|
+
* @param {Number} [angle=0] - Angle the object is rotated by
|
|
1176
|
+
* @param {Color} [color=Color()] - Color to apply to tile when rendered
|
|
1177
|
+
* @param {Number} [renderOrder=0] - Objects sorted by renderOrder before being rendered
|
|
984
1178
|
*/
|
|
985
|
-
constructor(pos=vec2(), size=vec2(1),
|
|
1179
|
+
constructor(pos=vec2(), size=vec2(1), tileInfo, angle=0, color, renderOrder=0)
|
|
986
1180
|
{
|
|
987
1181
|
// set passed in params
|
|
988
1182
|
ASSERT(isVector2(pos) && isVector2(size)); // ensure pos and size are vec2s
|
|
1183
|
+
ASSERT(typeof tileInfo !== 'number' || !tileInfo); // prevent old style calls
|
|
1184
|
+
// to fix old calls, replace with tile(tileIndex, tileSize)
|
|
989
1185
|
|
|
990
1186
|
/** @property {Vector2} - World space position of the object */
|
|
991
1187
|
this.pos = pos.copy();
|
|
@@ -993,10 +1189,8 @@ class EngineObject
|
|
|
993
1189
|
this.size = size;
|
|
994
1190
|
/** @property {Vector2} - Size of object used for drawing, uses size if not set */
|
|
995
1191
|
this.drawSize;
|
|
996
|
-
/** @property {
|
|
997
|
-
this.
|
|
998
|
-
/** @property {Vector2} - Size of tile in source pixels */
|
|
999
|
-
this.tileSize = tileSize;
|
|
1192
|
+
/** @property {TileInfo} - Tile info to render object (undefined is untextured) */
|
|
1193
|
+
this.tileInfo = tileInfo;
|
|
1000
1194
|
/** @property {Number} - Angle to rotate the object */
|
|
1001
1195
|
this.angle = angle;
|
|
1002
1196
|
/** @property {Color} - Color to apply when rendered */
|
|
@@ -1212,7 +1406,7 @@ class EngineObject
|
|
|
1212
1406
|
render()
|
|
1213
1407
|
{
|
|
1214
1408
|
// default object render
|
|
1215
|
-
drawTile(this.pos, this.drawSize || this.size, this.
|
|
1409
|
+
drawTile(this.pos, this.drawSize || this.size, this.tileInfo, this.color, this.angle, this.mirror, this.additiveColor);
|
|
1216
1410
|
}
|
|
1217
1411
|
|
|
1218
1412
|
/** Destroy this object, destroy it's children, detach it's parent, and mark it for removal */
|
|
@@ -1340,7 +1534,7 @@ class EngineObject
|
|
|
1340
1534
|
* @namespace Draw
|
|
1341
1535
|
*/
|
|
1342
1536
|
|
|
1343
|
-
|
|
1537
|
+
|
|
1344
1538
|
|
|
1345
1539
|
/** The primary 2D canvas visible to the user
|
|
1346
1540
|
* @type {HTMLCanvasElement}
|
|
@@ -1367,13 +1561,109 @@ let overlayContext;
|
|
|
1367
1561
|
* @memberof Draw */
|
|
1368
1562
|
let mainCanvasSize = vec2();
|
|
1369
1563
|
|
|
1370
|
-
/**
|
|
1371
|
-
* @type {
|
|
1564
|
+
/** Array containing texture info for batch rendering system
|
|
1565
|
+
* @type {Array}
|
|
1372
1566
|
* @memberof Draw */
|
|
1373
|
-
|
|
1567
|
+
let textureInfos = [];
|
|
1374
1568
|
|
|
1375
1569
|
// Engine internal variables not exposed to documentation
|
|
1376
|
-
let
|
|
1570
|
+
let drawCount;
|
|
1571
|
+
|
|
1572
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
1573
|
+
|
|
1574
|
+
/**
|
|
1575
|
+
* Create a tile info object
|
|
1576
|
+
* - This can take vecs or floats for easier use and conversion
|
|
1577
|
+
* - If an index is passed in, the tile size and index will determine the position
|
|
1578
|
+
* @param {(Number|Vector2)} [pos=Vector2()] - Top left corner of tile in pixels or index
|
|
1579
|
+
* @param {(Number|Vector2)} [size=tileSizeDefault] - Size of tile in pixels
|
|
1580
|
+
* @param {Number} [textureIndex=0] - Texture index to use
|
|
1581
|
+
* @return {TileInfo}
|
|
1582
|
+
* @example
|
|
1583
|
+
* tile(2) // a tile at index 2 using the default tile size of 16
|
|
1584
|
+
* tile(5, 8) // a tile at index 5 using a tile size of 8
|
|
1585
|
+
* tile(1, 16, 3) // a tile at index 1 of size 16 on texture 3
|
|
1586
|
+
* tile(vec2(4,8), vec2(30,10)) // a tile at pixel location (4,8) with a size of (30,10)
|
|
1587
|
+
* @memberof Draw
|
|
1588
|
+
*/
|
|
1589
|
+
function tile(pos=vec2(), size=tileSizeDefault, textureIndex=0)
|
|
1590
|
+
{
|
|
1591
|
+
// if size is a number, make it a vector
|
|
1592
|
+
if (size.x == undefined)
|
|
1593
|
+
{
|
|
1594
|
+
ASSERT(size > 0);
|
|
1595
|
+
size = vec2(size);
|
|
1596
|
+
}
|
|
1597
|
+
|
|
1598
|
+
// if pos is a number, use it as a tile index
|
|
1599
|
+
if (pos.x == undefined)
|
|
1600
|
+
{
|
|
1601
|
+
const textureInfo = textureInfos[textureIndex];
|
|
1602
|
+
if (textureInfo)
|
|
1603
|
+
{
|
|
1604
|
+
const cols = textureInfo.size.x / size.x |0;
|
|
1605
|
+
pos = vec2((pos%cols)*size.x, (pos/cols|0)*size.y);
|
|
1606
|
+
}
|
|
1607
|
+
else
|
|
1608
|
+
pos = vec2();
|
|
1609
|
+
}
|
|
1610
|
+
|
|
1611
|
+
// return a tile info object
|
|
1612
|
+
return new TileInfo(pos, size, textureIndex);
|
|
1613
|
+
}
|
|
1614
|
+
|
|
1615
|
+
/**
|
|
1616
|
+
* Tile Info - Stores info about how to draw a tile
|
|
1617
|
+
*/
|
|
1618
|
+
class TileInfo
|
|
1619
|
+
{
|
|
1620
|
+
/** Create a tile info object
|
|
1621
|
+
* @param {Vector2} [pos=Vector2()] - Top left corner of tile in pixels
|
|
1622
|
+
* @param {Vector2} [size=tileSizeDefault] - Size of tile in pixels
|
|
1623
|
+
* @param {Number} [textureIndex=0] - Texture index to use
|
|
1624
|
+
*/
|
|
1625
|
+
constructor(pos=vec2(), size=tileSizeDefault, textureIndex=0)
|
|
1626
|
+
{
|
|
1627
|
+
/** @property {Vector2} - Top left corner of tile in pixels */
|
|
1628
|
+
this.pos = pos;
|
|
1629
|
+
/** @property {Vector2} - Size of tile in pixels */
|
|
1630
|
+
this.size = size;
|
|
1631
|
+
/** @property {Number} - Texture index to use */
|
|
1632
|
+
this.textureIndex = textureIndex;
|
|
1633
|
+
}
|
|
1634
|
+
|
|
1635
|
+
/** Returns an offset copy of this tile, useful for animation
|
|
1636
|
+
* @param {Vector2} offset - Offset to apply in pixels
|
|
1637
|
+
* @return {TileInfo}
|
|
1638
|
+
*/
|
|
1639
|
+
offset(offset)
|
|
1640
|
+
{ return new TileInfo(this.pos.add(offset), this.size, this.textureIndex); }
|
|
1641
|
+
|
|
1642
|
+
/** Returns the texture info for this tile
|
|
1643
|
+
* @return {TextureInfo}
|
|
1644
|
+
*/
|
|
1645
|
+
getTextureInfo()
|
|
1646
|
+
{ return textureInfos[this.textureIndex]; }
|
|
1647
|
+
}
|
|
1648
|
+
|
|
1649
|
+
/** Texture Info - Stores info about each texture */
|
|
1650
|
+
class TextureInfo
|
|
1651
|
+
{
|
|
1652
|
+
// create a TextureInfo, called automatically by the engine
|
|
1653
|
+
constructor(image)
|
|
1654
|
+
{
|
|
1655
|
+
/** @property {CanvasImageSource} - image source */
|
|
1656
|
+
this.image = image;
|
|
1657
|
+
/** @property {Vector2} - size of the image */
|
|
1658
|
+
this.size = vec2(image.width, image.height);
|
|
1659
|
+
/** @property {WebGLTexture} - webgl texture */
|
|
1660
|
+
this.glTexture = glEnable && glCreateTexture(image);
|
|
1661
|
+
/** @property {Vector2} - size to adjust tile to fix bleeding */
|
|
1662
|
+
this.fixBleedSize = vec2(tileFixBleedScale).divide(this.size);
|
|
1663
|
+
}
|
|
1664
|
+
}
|
|
1665
|
+
|
|
1666
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
1377
1667
|
|
|
1378
1668
|
/** Convert from screen to world space coordinates
|
|
1379
1669
|
* @param {Vector2} screenPos
|
|
@@ -1404,7 +1694,7 @@ function worldToScreen(worldPos)
|
|
|
1404
1694
|
/** Draw textured tile centered in world space, with color applied if using WebGL
|
|
1405
1695
|
* @param {Vector2} pos - Center of the tile in world space
|
|
1406
1696
|
* @param {Vector2} [size=Vector2(1,1)] - Size of the tile in world space
|
|
1407
|
-
* @param {
|
|
1697
|
+
* @param {TileInfo}[tileInfo] - Tile info to use, untextured if undefined
|
|
1408
1698
|
* @param {Vector2} [tileSize=tileSizeDefault] - Tile size in source pixels
|
|
1409
1699
|
* @param {Color} [color=Color()] - Color to modulate with
|
|
1410
1700
|
* @param {Number} [angle=0] - Angle to rotate by
|
|
@@ -1413,11 +1703,14 @@ function worldToScreen(worldPos)
|
|
|
1413
1703
|
* @param {Boolean} [useWebGL=glEnable] - Use accelerated WebGL rendering
|
|
1414
1704
|
* @param {Boolean} [screenSpace=0] - If true the pos and size are in screen space
|
|
1415
1705
|
* @memberof Draw */
|
|
1416
|
-
function drawTile(pos, size=vec2(1),
|
|
1706
|
+
function drawTile(pos, size=vec2(1), tileInfo, color=new Color,
|
|
1417
1707
|
angle=0, mirror, additiveColor=new Color(0,0,0,0), useWebGL=glEnable, screenSpace)
|
|
1418
1708
|
{
|
|
1709
|
+
ASSERT(typeof tileInfo !== 'number' || !tileInfo); // prevent old style calls
|
|
1710
|
+
// to fix old calls, replace with tile(tileIndex, tileSize)
|
|
1711
|
+
|
|
1419
1712
|
showWatermark && ++drawCount;
|
|
1420
|
-
|
|
1713
|
+
const textureInfo = tileInfo && tileInfo.getTextureInfo();
|
|
1421
1714
|
if (glEnable && useWebGL)
|
|
1422
1715
|
{
|
|
1423
1716
|
if (screenSpace)
|
|
@@ -1426,46 +1719,48 @@ function drawTile(pos, size=vec2(1), tileIndex=-1, tileSize=tileSizeDefault, col
|
|
|
1426
1719
|
pos = screenToWorld(pos);
|
|
1427
1720
|
size = size.scale(1/cameraScale);
|
|
1428
1721
|
}
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
// if negative tile index or image not found, force untextured
|
|
1432
|
-
glDraw(pos.x, pos.y, size.x, size.y, angle, 0, 0, 0, 0, 0, color.rgbaInt());
|
|
1433
|
-
}
|
|
1434
|
-
else
|
|
1722
|
+
|
|
1723
|
+
if (textureInfo)
|
|
1435
1724
|
{
|
|
1436
1725
|
// calculate uvs and render
|
|
1437
|
-
const
|
|
1438
|
-
const
|
|
1439
|
-
const
|
|
1440
|
-
const
|
|
1441
|
-
|
|
1726
|
+
const x = tileInfo.pos.x / textureInfo.size.x;
|
|
1727
|
+
const y = tileInfo.pos.y / textureInfo.size.y;
|
|
1728
|
+
const w = tileInfo.size.x / textureInfo.size.x;
|
|
1729
|
+
const h = tileInfo.size.y / textureInfo.size.y;
|
|
1730
|
+
const tileImageFixBleed = textureInfo.fixBleedSize;
|
|
1731
|
+
glSetTexture(textureInfo.glTexture);
|
|
1442
1732
|
glDraw(pos.x, pos.y, mirror ? -size.x : size.x, size.y, angle,
|
|
1443
|
-
|
|
1444
|
-
|
|
1733
|
+
x + tileImageFixBleed.x, y + tileImageFixBleed.y,
|
|
1734
|
+
x - tileImageFixBleed.x + w, y - tileImageFixBleed.y + h,
|
|
1445
1735
|
color.rgbaInt(), additiveColor.rgbaInt());
|
|
1446
1736
|
}
|
|
1737
|
+
else
|
|
1738
|
+
{
|
|
1739
|
+
// if no tile info, force untextured
|
|
1740
|
+
glDraw(pos.x, pos.y, size.x, size.y, angle, 0, 0, 0, 0, 0, color.rgbaInt());
|
|
1741
|
+
}
|
|
1447
1742
|
}
|
|
1448
1743
|
else
|
|
1449
1744
|
{
|
|
1450
1745
|
// normal canvas 2D rendering method (slower)
|
|
1451
1746
|
drawCanvas2D(pos, size, angle, mirror, (context)=>
|
|
1452
1747
|
{
|
|
1453
|
-
if (
|
|
1748
|
+
if (textureInfo)
|
|
1454
1749
|
{
|
|
1455
|
-
//
|
|
1456
|
-
|
|
1457
|
-
|
|
1750
|
+
// calculate uvs and render
|
|
1751
|
+
const x = tileInfo.pos.x + tileFixBleedScale;
|
|
1752
|
+
const y = tileInfo.pos.y + tileFixBleedScale;
|
|
1753
|
+
const w = tileInfo.size.x - 2*tileFixBleedScale;
|
|
1754
|
+
const h = tileInfo.size.y - 2*tileFixBleedScale;
|
|
1755
|
+
context.globalAlpha = color.a; // only alpha is supported
|
|
1756
|
+
context.drawImage(textureInfo.image, x, y, w, h, -.5, -.5, 1, 1);
|
|
1757
|
+
context.globalAlpha = 1; // set back to full alpha
|
|
1458
1758
|
}
|
|
1459
1759
|
else
|
|
1460
1760
|
{
|
|
1461
|
-
//
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
const sY = (tileIndex/cols|0)*tileSize.y + tileFixBleedScale;
|
|
1465
|
-
const sWidth = tileSize.x - 2*tileFixBleedScale;
|
|
1466
|
-
const sHeight = tileSize.y - 2*tileFixBleedScale;
|
|
1467
|
-
context.globalAlpha = color.a; // only alpha is supported
|
|
1468
|
-
context.drawImage(tileImage, sX, sY, sWidth, sHeight, -.5, -.5, 1, 1);
|
|
1761
|
+
// if no tile info, force untextured
|
|
1762
|
+
context.fillStyle = color;
|
|
1763
|
+
context.fillRect(-.5, -.5, 1, 1);
|
|
1469
1764
|
}
|
|
1470
1765
|
}, undefined, screenSpace);
|
|
1471
1766
|
}
|
|
@@ -1480,7 +1775,7 @@ function drawTile(pos, size=vec2(1), tileIndex=-1, tileSize=tileSizeDefault, col
|
|
|
1480
1775
|
* @param {Boolean} [screenSpace=0]
|
|
1481
1776
|
* @memberof Draw */
|
|
1482
1777
|
function drawRect(pos, size, color, angle, useWebGL, screenSpace)
|
|
1483
|
-
{ drawTile(pos, size,
|
|
1778
|
+
{ drawTile(pos, size, undefined, color, angle, 0, undefined, useWebGL, screenSpace); }
|
|
1484
1779
|
|
|
1485
1780
|
/** Draw colored polygon using passed in points
|
|
1486
1781
|
* @param {Array} points - Array of Vector2 points
|
|
@@ -1531,12 +1826,12 @@ function drawCanvas2D(pos, size, angle, mirror, drawFunction, context = mainCont
|
|
|
1531
1826
|
{
|
|
1532
1827
|
if (!screenSpace)
|
|
1533
1828
|
{
|
|
1534
|
-
//
|
|
1829
|
+
// transform from world space to screen space
|
|
1535
1830
|
pos = worldToScreen(pos);
|
|
1536
1831
|
size = size.scale(cameraScale);
|
|
1537
1832
|
}
|
|
1538
1833
|
context.save();
|
|
1539
|
-
context.translate(pos.x+.5
|
|
1834
|
+
context.translate(pos.x+.5, pos.y+.5);
|
|
1540
1835
|
context.rotate(angle);
|
|
1541
1836
|
context.scale(mirror ? -size.x : size.x, size.y);
|
|
1542
1837
|
drawFunction(context);
|
|
@@ -1625,10 +1920,9 @@ class FontImage
|
|
|
1625
1920
|
* @param {HTMLImageElement} [image] - Image for the font, if undefined default font is used
|
|
1626
1921
|
* @param {Vector2} [tileSize=vec2(8)] - Size of the font source tiles
|
|
1627
1922
|
* @param {Vector2} [paddingSize=vec2(0,1)] - How much extra space to add between characters
|
|
1628
|
-
* @param {Number} [startTileIndex=0] - Tile index in image where font starts
|
|
1629
1923
|
* @param {CanvasRenderingContext2D} [context=overlayContext] - context to draw to
|
|
1630
1924
|
*/
|
|
1631
|
-
constructor(image, tileSize=vec2(8), paddingSize=vec2(0,1),
|
|
1925
|
+
constructor(image, tileSize=vec2(8), paddingSize=vec2(0,1), context=overlayContext)
|
|
1632
1926
|
{
|
|
1633
1927
|
// load default font image
|
|
1634
1928
|
if (!engineFontImage)
|
|
@@ -1637,7 +1931,6 @@ class FontImage
|
|
|
1637
1931
|
this.image = image || engineFontImage;
|
|
1638
1932
|
this.tileSize = tileSize;
|
|
1639
1933
|
this.paddingSize = paddingSize;
|
|
1640
|
-
this.startTileIndex = startTileIndex;
|
|
1641
1934
|
this.context = context;
|
|
1642
1935
|
}
|
|
1643
1936
|
|
|
@@ -1678,7 +1971,7 @@ class FontImage
|
|
|
1678
1971
|
charCode = 127; // unknown character
|
|
1679
1972
|
|
|
1680
1973
|
// get the character source location and draw it
|
|
1681
|
-
const tile =
|
|
1974
|
+
const tile = charCode - 32;
|
|
1682
1975
|
const x = tile % cols;
|
|
1683
1976
|
const y = tile / cols |0;
|
|
1684
1977
|
const drawPos = pos.add(vec2(j,i).multiply(drawSize));
|
|
@@ -1710,8 +2003,7 @@ function toggleFullscreen()
|
|
|
1710
2003
|
}
|
|
1711
2004
|
else if (document.body.requestFullscreen)
|
|
1712
2005
|
document.body.requestFullscreen();
|
|
1713
|
-
}
|
|
1714
|
-
|
|
2006
|
+
}
|
|
1715
2007
|
/**
|
|
1716
2008
|
* LittleJS Input System
|
|
1717
2009
|
* - Tracks keyboard down, pressed, and released
|
|
@@ -1721,7 +2013,7 @@ function toggleFullscreen()
|
|
|
1721
2013
|
* @namespace Input
|
|
1722
2014
|
*/
|
|
1723
2015
|
|
|
1724
|
-
|
|
2016
|
+
|
|
1725
2017
|
|
|
1726
2018
|
/** Returns true if device key is down
|
|
1727
2019
|
* @param {Number} key
|
|
@@ -1905,18 +2197,26 @@ function mouseToScreen(mousePos)
|
|
|
1905
2197
|
const stickData = [];
|
|
1906
2198
|
function gamepadsUpdate()
|
|
1907
2199
|
{
|
|
1908
|
-
|
|
2200
|
+
// update touch gamepad if enabled
|
|
2201
|
+
if (touchGamepadEnable && isTouchDevice)
|
|
1909
2202
|
{
|
|
1910
|
-
//
|
|
1911
|
-
|
|
1912
|
-
|
|
2203
|
+
// create the touch gamepad if it doesn't exist
|
|
2204
|
+
if (!touchGamepadButtons)
|
|
2205
|
+
createTouchGamepad();
|
|
1913
2206
|
|
|
1914
|
-
|
|
1915
|
-
const data = inputData[1] || (inputData[1] = []);
|
|
1916
|
-
for (let i=10; i--;)
|
|
2207
|
+
if (touchGamepadTimer.isSet())
|
|
1917
2208
|
{
|
|
1918
|
-
|
|
1919
|
-
|
|
2209
|
+
// read virtual analog stick
|
|
2210
|
+
const sticks = stickData[0] || (stickData[0] = []);
|
|
2211
|
+
sticks[0] = vec2(touchGamepadStick.x, -touchGamepadStick.y); // flip vertical
|
|
2212
|
+
|
|
2213
|
+
// read virtual gamepad buttons
|
|
2214
|
+
const data = inputData[1] || (inputData[1] = []);
|
|
2215
|
+
for (let i=10; i--;)
|
|
2216
|
+
{
|
|
2217
|
+
const j = i == 3 ? 2 : i == 2 ? 3 : i; // fix button locations
|
|
2218
|
+
data[j] = touchGamepadButtons[i] ? 1 + 2*!gamepadIsDown(j,0) : 4*gamepadIsDown(j,0);
|
|
2219
|
+
}
|
|
1920
2220
|
}
|
|
1921
2221
|
}
|
|
1922
2222
|
|
|
@@ -2013,6 +2313,10 @@ if (isTouchDevice)
|
|
|
2013
2313
|
// set was touching
|
|
2014
2314
|
wasTouching = touching;
|
|
2015
2315
|
|
|
2316
|
+
// prevent default handling like copy and magnifier lens
|
|
2317
|
+
if (document.hasFocus()) // allow document to get focus
|
|
2318
|
+
e.preventDefault();
|
|
2319
|
+
|
|
2016
2320
|
// must return true so the document will get focus
|
|
2017
2321
|
return true;
|
|
2018
2322
|
}
|
|
@@ -2025,7 +2329,7 @@ if (isTouchDevice)
|
|
|
2025
2329
|
let touchGamepadTimer = new Timer, touchGamepadButtons, touchGamepadStick;
|
|
2026
2330
|
|
|
2027
2331
|
// create the touch gamepad, called automatically by the engine
|
|
2028
|
-
|
|
2332
|
+
function createTouchGamepad()
|
|
2029
2333
|
{
|
|
2030
2334
|
// touch input internal variables
|
|
2031
2335
|
touchGamepadButtons = [];
|
|
@@ -2159,7 +2463,7 @@ function touchGamepadRender()
|
|
|
2159
2463
|
* @namespace Audio
|
|
2160
2464
|
*/
|
|
2161
2465
|
|
|
2162
|
-
|
|
2466
|
+
|
|
2163
2467
|
|
|
2164
2468
|
/**
|
|
2165
2469
|
* Sound Object - Stores a zzfx sound for later use and can be played positionally
|
|
@@ -2195,8 +2499,9 @@ class Sound
|
|
|
2195
2499
|
if (zzfxSound)
|
|
2196
2500
|
{
|
|
2197
2501
|
// generate zzfx sound now for fast playback
|
|
2198
|
-
this.randomness = zzfxSound[1] || (zzfxSound[1] = 0);
|
|
2199
|
-
this.
|
|
2502
|
+
this.randomness = zzfxSound[1] || (zzfxSound[1] = 0);
|
|
2503
|
+
this.sampleChannels = [zzfxG(...zzfxSound)];
|
|
2504
|
+
this.sampleRate = zzfxR;
|
|
2200
2505
|
}
|
|
2201
2506
|
}
|
|
2202
2507
|
|
|
@@ -2205,11 +2510,12 @@ class Sound
|
|
|
2205
2510
|
* @param {Number} [volume=1] - How much to scale volume by (in addition to range fade)
|
|
2206
2511
|
* @param {Number} [pitch=1] - How much to scale pitch by (also adjusted by this.randomness)
|
|
2207
2512
|
* @param {Number} [randomnessScale=1] - How much to scale randomness
|
|
2208
|
-
* @
|
|
2513
|
+
* @param {Boolean} [loop=0] - Should the sound loop
|
|
2514
|
+
* @return {AudioBufferSourceNode} - The audio source node
|
|
2209
2515
|
*/
|
|
2210
|
-
play(pos, volume=1, pitch=1, randomnessScale=1)
|
|
2516
|
+
play(pos, volume=1, pitch=1, randomnessScale=1, loop=0)
|
|
2211
2517
|
{
|
|
2212
|
-
if (!soundEnable || !this.
|
|
2518
|
+
if (!soundEnable || !this.sampleChannels) return;
|
|
2213
2519
|
|
|
2214
2520
|
let pan;
|
|
2215
2521
|
if (pos)
|
|
@@ -2232,43 +2538,80 @@ class Sound
|
|
|
2232
2538
|
|
|
2233
2539
|
// play the sound
|
|
2234
2540
|
const playbackRate = pitch + pitch * this.randomness*randomnessScale*rand(-1,1);
|
|
2235
|
-
return playSamples(
|
|
2541
|
+
return this.source = playSamples(this.sampleChannels, volume, playbackRate, pan, loop, this.sampleRate);
|
|
2542
|
+
}
|
|
2543
|
+
|
|
2544
|
+
/** Stop the last instance of this sound that was played */
|
|
2545
|
+
stop()
|
|
2546
|
+
{
|
|
2547
|
+
if (this.source)
|
|
2548
|
+
this.source.stop();
|
|
2549
|
+
this.source = 0;
|
|
2236
2550
|
}
|
|
2237
2551
|
|
|
2238
2552
|
/** Play the sound as a note with a semitone offset
|
|
2239
2553
|
* @param {Number} semitoneOffset - How many semitones to offset pitch
|
|
2240
2554
|
* @param {Vector2} [pos] - World space position to play the sound, sound is not attenuated if null
|
|
2241
2555
|
* @param {Number} [volume=1] - How much to scale volume by (in addition to range fade)
|
|
2242
|
-
* @return {AudioBufferSourceNode} - The audio
|
|
2556
|
+
* @return {AudioBufferSourceNode} - The audio source node
|
|
2243
2557
|
*/
|
|
2244
2558
|
playNote(semitoneOffset, pos, volume)
|
|
2245
2559
|
{ return this.play(pos, volume, 2**(semitoneOffset/12), 0); }
|
|
2560
|
+
|
|
2561
|
+
/** Get how long this sound is in seconds
|
|
2562
|
+
* @return {Number} - How long the sound is in seconds (undefined if loading)
|
|
2563
|
+
*/
|
|
2564
|
+
getDuration()
|
|
2565
|
+
{ return this.sampleChannels && this.sampleChannels[0].length / this.sampleRate; }
|
|
2566
|
+
|
|
2567
|
+
/** Check if the last instance of this sound is playing
|
|
2568
|
+
* @return {Boolean} - True if the sound is playing
|
|
2569
|
+
*/
|
|
2570
|
+
isPlaying() { return this.source && !this.source.ended; }
|
|
2571
|
+
|
|
2572
|
+
/** Check if sound is loading, for sounds fetched from a url
|
|
2573
|
+
* @return {Boolean} - True if sound is loading and not ready to play
|
|
2574
|
+
*/
|
|
2575
|
+
isLoading() { return !this.sampleChannels; }
|
|
2246
2576
|
}
|
|
2247
2577
|
|
|
2248
2578
|
/**
|
|
2249
2579
|
* Sound Wave Object - Stores a wave sound for later use and can be played positionally
|
|
2580
|
+
* - this can be used to play wave, mp3, and ogg files
|
|
2581
|
+
* @example
|
|
2582
|
+
* // create a sound
|
|
2583
|
+
* const sound_example = new SoundWave('sound.mp3');
|
|
2584
|
+
*
|
|
2585
|
+
* // play the sound
|
|
2586
|
+
* sound_example.play();
|
|
2250
2587
|
*/
|
|
2251
2588
|
class SoundWave extends Sound
|
|
2252
2589
|
{
|
|
2253
2590
|
/** Create a sound object and cache the wave file for later use
|
|
2254
|
-
* @param {String}
|
|
2255
|
-
* @param {Number} [randomness
|
|
2591
|
+
* @param {String} filename - Filename of audio file to load
|
|
2592
|
+
* @param {Number} [randomness=0] - How much to randomize frequency each time sound plays
|
|
2256
2593
|
* @param {Number} [range=soundDefaultRange] - World space max range of sound, will not play if camera is farther away
|
|
2257
2594
|
* @param {Number} [taper=soundDefaultTaper] - At what percentage of range should it start tapering off
|
|
2258
2595
|
*/
|
|
2259
|
-
constructor(
|
|
2596
|
+
constructor(filename, randomness=0, range, taper)
|
|
2260
2597
|
{
|
|
2261
2598
|
super(0, range, taper);
|
|
2262
2599
|
this.randomness = randomness;
|
|
2263
2600
|
|
|
2264
2601
|
if (!soundEnable) return;
|
|
2265
|
-
if (!
|
|
2602
|
+
if (!soundDecoderContext)
|
|
2266
2603
|
soundDecoderContext = new AudioContext;
|
|
2267
2604
|
|
|
2268
|
-
fetch(
|
|
2605
|
+
fetch(filename)
|
|
2269
2606
|
.then(response => response.arrayBuffer())
|
|
2270
|
-
.then(arrayBuffer =>
|
|
2271
|
-
.then(audioBuffer =>
|
|
2607
|
+
.then(arrayBuffer => soundDecoderContext.decodeAudioData(arrayBuffer))
|
|
2608
|
+
.then(audioBuffer =>
|
|
2609
|
+
{
|
|
2610
|
+
this.sampleChannels = [];
|
|
2611
|
+
for (let i = audioBuffer.numberOfChannels; i--;)
|
|
2612
|
+
this.sampleChannels[i] = audioBuffer.getChannelData(i);
|
|
2613
|
+
this.sampleRate = audioBuffer.sampleRate;
|
|
2614
|
+
});
|
|
2272
2615
|
}
|
|
2273
2616
|
}
|
|
2274
2617
|
let soundDecoderContext; // audio context used only to decode audio files
|
|
@@ -2303,48 +2646,34 @@ let soundDecoderContext; // audio context used only to decode audio files
|
|
|
2303
2646
|
* // play the music
|
|
2304
2647
|
* music_example.play();
|
|
2305
2648
|
*/
|
|
2306
|
-
class Music
|
|
2649
|
+
class Music extends Sound
|
|
2307
2650
|
{
|
|
2308
2651
|
/** Create a music object and cache the zzfx music samples for later use
|
|
2309
2652
|
* @param {Array} zzfxMusic - Array of zzfx music parameters
|
|
2310
2653
|
*/
|
|
2311
2654
|
constructor(zzfxMusic)
|
|
2312
2655
|
{
|
|
2313
|
-
|
|
2656
|
+
super();
|
|
2314
2657
|
|
|
2315
|
-
|
|
2658
|
+
if (!soundEnable) return;
|
|
2659
|
+
this.randomness = 0;
|
|
2660
|
+
this.sampleChannels = zzfxM(...zzfxMusic);
|
|
2661
|
+
this.sampleRate = zzfxR;
|
|
2316
2662
|
}
|
|
2317
2663
|
|
|
2318
2664
|
/** Play the music
|
|
2319
2665
|
* @param {Number} [volume=1] - How much to scale volume by
|
|
2320
|
-
* @param {Boolean} [loop=1] - True if the music should loop
|
|
2321
|
-
* @return {AudioBufferSourceNode} - The audio node
|
|
2666
|
+
* @param {Boolean} [loop=1] - True if the music should loop
|
|
2667
|
+
* @return {AudioBufferSourceNode} - The audio source node
|
|
2322
2668
|
*/
|
|
2323
2669
|
play(volume, loop = 1)
|
|
2324
|
-
{
|
|
2325
|
-
if (!soundEnable) return;
|
|
2326
|
-
|
|
2327
|
-
return this.source = playSamples(this.cachedSamples, volume, 1, 0, loop);
|
|
2328
|
-
}
|
|
2329
|
-
|
|
2330
|
-
/** Stop the music */
|
|
2331
|
-
stop()
|
|
2332
|
-
{
|
|
2333
|
-
if (this.source)
|
|
2334
|
-
this.source.stop();
|
|
2335
|
-
this.source = 0;
|
|
2336
|
-
}
|
|
2337
|
-
|
|
2338
|
-
/** Check if music is playing
|
|
2339
|
-
* @return {Boolean}
|
|
2340
|
-
*/
|
|
2341
|
-
isPlaying() { return this.source; }
|
|
2670
|
+
{ return super.play(0, volume, 1, 1, loop); }
|
|
2342
2671
|
}
|
|
2343
2672
|
|
|
2344
|
-
/** Play an mp3 or wav audio from a local file or url
|
|
2673
|
+
/** Play an mp3, ogg, or wav audio from a local file or url
|
|
2345
2674
|
* @param {String} url - Location of sound file to play
|
|
2346
2675
|
* @param {Number} [volume=1] - How much to scale volume by
|
|
2347
|
-
* @param {Boolean} [loop=1] - True if the music should loop
|
|
2676
|
+
* @param {Boolean} [loop=1] - True if the music should loop
|
|
2348
2677
|
* @return {HTMLAudioElement} - The audio element for this sound
|
|
2349
2678
|
* @memberof Audio */
|
|
2350
2679
|
function playAudioFile(url, volume=1, loop=1)
|
|
@@ -2408,9 +2737,10 @@ let audioContext;
|
|
|
2408
2737
|
* @param {Number} [rate=1] - The playback rate to use
|
|
2409
2738
|
* @param {Number} [pan=0] - How much to apply stereo panning
|
|
2410
2739
|
* @param {Boolean} [loop=0] - True if the sound should loop when it reaches the end
|
|
2740
|
+
* @param {Number} [sampleRate=44100] - Sample rate for the sound
|
|
2411
2741
|
* @return {AudioBufferSourceNode} - The audio node of the sound played
|
|
2412
2742
|
* @memberof Audio */
|
|
2413
|
-
function playSamples(sampleChannels, volume=1, rate=1, pan=0, loop=0)
|
|
2743
|
+
function playSamples(sampleChannels, volume=1, rate=1, pan=0, loop=0, sampleRate=zzfxR)
|
|
2414
2744
|
{
|
|
2415
2745
|
if (!soundEnable) return;
|
|
2416
2746
|
|
|
@@ -2427,7 +2757,7 @@ function playSamples(sampleChannels, volume=1, rate=1, pan=0, loop=0)
|
|
|
2427
2757
|
}
|
|
2428
2758
|
|
|
2429
2759
|
// create buffer and source
|
|
2430
|
-
const buffer = audioContext.createBuffer(sampleChannels.length, sampleChannels[0].length,
|
|
2760
|
+
const buffer = audioContext.createBuffer(sampleChannels.length, sampleChannels[0].length, sampleRate),
|
|
2431
2761
|
source = audioContext.createBufferSource();
|
|
2432
2762
|
|
|
2433
2763
|
// copy samples to buffer and setup source
|
|
@@ -2686,7 +3016,7 @@ function zzfxM(instruments, patterns, sequence, BPM = 125)
|
|
|
2686
3016
|
* @namespace TileCollision
|
|
2687
3017
|
*/
|
|
2688
3018
|
|
|
2689
|
-
|
|
3019
|
+
|
|
2690
3020
|
|
|
2691
3021
|
/** The tile collision layer array, use setTileCollisionData and getTileCollisionData to access
|
|
2692
3022
|
* @type {Array}
|
|
@@ -2831,7 +3161,7 @@ class TileLayerData
|
|
|
2831
3161
|
}
|
|
2832
3162
|
|
|
2833
3163
|
/**
|
|
2834
|
-
* Tile
|
|
3164
|
+
* Tile Layer - cached rendering system for tile layers
|
|
2835
3165
|
* - Each Tile layer is rendered to an off screen canvas
|
|
2836
3166
|
* - To allow dynamic modifications, layers are rendered using canvas 2d
|
|
2837
3167
|
* - Some devices like mobile phones are limited to 4k texture resolution
|
|
@@ -2847,13 +3177,13 @@ class TileLayer extends EngineObject
|
|
|
2847
3177
|
/** Create a tile layer object
|
|
2848
3178
|
* @param {Vector2} [position=Vector2()] - World space position
|
|
2849
3179
|
* @param {Vector2} [size=tileCollisionSize] - World space size
|
|
2850
|
-
* @param {
|
|
3180
|
+
* @param {TileInfo} [tileInfo] - Tile info for layer
|
|
2851
3181
|
* @param {Vector2} [scale=Vector2(1,1)] - How much to scale this layer when rendered
|
|
2852
3182
|
* @param {Number} [renderOrder=0] - Objects sorted by renderOrder before being rendered
|
|
2853
3183
|
*/
|
|
2854
|
-
constructor(pos, size=tileCollisionSize,
|
|
3184
|
+
constructor(pos, size=tileCollisionSize, tileInfo=tile(), scale=vec2(1), renderOrder=0)
|
|
2855
3185
|
{
|
|
2856
|
-
super(pos, size,
|
|
3186
|
+
super(pos, size, tileInfo, 0, undefined, renderOrder);
|
|
2857
3187
|
|
|
2858
3188
|
/** @property {HTMLCanvasElement} - The canvas used by this tile layer */
|
|
2859
3189
|
this.canvas = document.createElement('canvas');
|
|
@@ -2930,13 +3260,13 @@ constructor(pos, size=tileCollisionSize, tileSize=tileSizeDefault, scale=vec2(1)
|
|
|
2930
3260
|
mainCanvas = this.canvas;
|
|
2931
3261
|
mainContext = this.context;
|
|
2932
3262
|
cameraPos = this.size.scale(.5);
|
|
2933
|
-
cameraScale = this.
|
|
3263
|
+
cameraScale = this.tileInfo.size.x;
|
|
2934
3264
|
|
|
2935
3265
|
if (clear)
|
|
2936
3266
|
{
|
|
2937
3267
|
// clear and set size
|
|
2938
|
-
mainCanvas.width = this.size.x * this.
|
|
2939
|
-
mainCanvas.height = this.size.y * this.
|
|
3268
|
+
mainCanvas.width = this.size.x * this.tileInfo.size.x;
|
|
3269
|
+
mainCanvas.height = this.size.y * this.tileInfo.size.y;
|
|
2940
3270
|
}
|
|
2941
3271
|
|
|
2942
3272
|
// begin a new render for the tile canvas
|
|
@@ -2967,7 +3297,8 @@ constructor(pos, size=tileCollisionSize, tileSize=tileSizeDefault, scale=vec2(1)
|
|
|
2967
3297
|
if (d.tile != undefined)
|
|
2968
3298
|
{
|
|
2969
3299
|
ASSERT(mainContext == this.context); // must call redrawStart() before drawing tiles
|
|
2970
|
-
|
|
3300
|
+
const tileInfo = tile(d.tile, this.tileInfo.size, this.tileInfo.textureIndex);
|
|
3301
|
+
drawTile(pos, vec2(1), tileInfo, d.color, d.direction*PI/2, d.mirror);
|
|
2971
3302
|
}
|
|
2972
3303
|
}
|
|
2973
3304
|
|
|
@@ -2989,8 +3320,8 @@ constructor(pos, size=tileCollisionSize, tileSize=tileSizeDefault, scale=vec2(1)
|
|
|
2989
3320
|
{
|
|
2990
3321
|
const context = this.context;
|
|
2991
3322
|
context.save();
|
|
2992
|
-
pos = pos.subtract(this.pos).multiply(this.
|
|
2993
|
-
size = size.multiply(this.
|
|
3323
|
+
pos = pos.subtract(this.pos).multiply(this.tileInfo.size);
|
|
3324
|
+
size = size.multiply(this.tileInfo.size);
|
|
2994
3325
|
context.translate(pos.x, this.canvas.height - pos.y);
|
|
2995
3326
|
context.rotate(angle);
|
|
2996
3327
|
context.scale(mirror ? -size.x : size.x, size.y);
|
|
@@ -3001,28 +3332,28 @@ constructor(pos, size=tileCollisionSize, tileSize=tileSizeDefault, scale=vec2(1)
|
|
|
3001
3332
|
/** Draw a tile directly onto the layer canvas
|
|
3002
3333
|
* @param {Vector2} pos
|
|
3003
3334
|
* @param {Vector2} [size=Vector2(1,1)]
|
|
3004
|
-
* @param {
|
|
3005
|
-
* @param {Vector2} [tileSize=tileSizeDefault]
|
|
3335
|
+
* @param {TileInfo} [tileInfo]
|
|
3006
3336
|
* @param {Color} [color=Color()]
|
|
3007
3337
|
* @param {Number} [angle=0]
|
|
3008
3338
|
* @param {Boolean} [mirror=0] */
|
|
3009
|
-
drawTile(pos, size=vec2(1),
|
|
3339
|
+
drawTile(pos, size=vec2(1), tileInfo, color=new Color, angle, mirror)
|
|
3010
3340
|
{
|
|
3011
3341
|
this.drawCanvas2D(pos, size, angle, mirror, (context)=>
|
|
3012
3342
|
{
|
|
3013
|
-
|
|
3343
|
+
const textureInfo = tileInfo && tileInfo.getTextureInfo();
|
|
3344
|
+
if (textureInfo)
|
|
3014
3345
|
{
|
|
3015
|
-
//
|
|
3016
|
-
context.
|
|
3017
|
-
|
|
3346
|
+
context.globalAlpha = color.a; // only alpha is supported
|
|
3347
|
+
context.drawImage(textureInfo.image,
|
|
3348
|
+
tileInfo.pos.x, tileInfo.pos.y,
|
|
3349
|
+
tileInfo.size.x, tileInfo.size.y, -.5, -.5, 1, 1);
|
|
3350
|
+
context.globalAlpha = 1;
|
|
3018
3351
|
}
|
|
3019
3352
|
else
|
|
3020
3353
|
{
|
|
3021
|
-
|
|
3022
|
-
context.
|
|
3023
|
-
context.
|
|
3024
|
-
(tileIndex%cols)*tileSize.x, (tileIndex/cols|0)*tileSize.y,
|
|
3025
|
-
tileSize.x, tileSize.y, -.5, -.5, 1, 1);
|
|
3354
|
+
// untextured
|
|
3355
|
+
context.fillStyle = color;
|
|
3356
|
+
context.fillRect(-.5, -.5, 1, 1);
|
|
3026
3357
|
}
|
|
3027
3358
|
});
|
|
3028
3359
|
}
|
|
@@ -3039,7 +3370,7 @@ constructor(pos, size=tileCollisionSize, tileSize=tileSizeDefault, scale=vec2(1)
|
|
|
3039
3370
|
* LittleJS Particle System
|
|
3040
3371
|
*/
|
|
3041
3372
|
|
|
3042
|
-
|
|
3373
|
+
|
|
3043
3374
|
|
|
3044
3375
|
/**
|
|
3045
3376
|
* Particle Emitter - Spawns particles with the given settings
|
|
@@ -3050,7 +3381,7 @@ constructor(pos, size=tileCollisionSize, tileSize=tileSizeDefault, scale=vec2(1)
|
|
|
3050
3381
|
* let particleEmiter = new ParticleEmitter
|
|
3051
3382
|
* (
|
|
3052
3383
|
* pos, 0, 1, 0, 500, PI, // pos, angle, emitSize, emitTime, emitRate, emiteCone
|
|
3053
|
-
* 0,
|
|
3384
|
+
* tile(0, 16), // tileInfo
|
|
3054
3385
|
* new Color(1,1,1), new Color(0,0,0), // colorStartA, colorStartB
|
|
3055
3386
|
* new Color(1,1,1,0), new Color(0,0,0,0), // colorEndA, colorEndB
|
|
3056
3387
|
* 2, .2, .2, .1, .05, // particleTime, sizeStart, sizeEnd, particleSpeed, particleAngleSpeed
|
|
@@ -3067,8 +3398,7 @@ class ParticleEmitter extends EngineObject
|
|
|
3067
3398
|
* @param {Number} [emitTime=0] - How long to stay alive (0 is forever)
|
|
3068
3399
|
* @param {Number} [emitRate=100] - How many particles per second to spawn, does not emit if 0
|
|
3069
3400
|
* @param {Number} [emitConeAngle=PI] - Local angle to apply velocity to particles from emitter
|
|
3070
|
-
* @param {
|
|
3071
|
-
* @param {Vector2} [tileSize=tileSizeDefault] - Tile size for particles
|
|
3401
|
+
* @param {TileInfo} [tileInfo] - Tile info to render particles (undefined is untextured)
|
|
3072
3402
|
* @param {Color} [colorStartA=Color()] - Color at start of life 1, randomized between start colors
|
|
3073
3403
|
* @param {Color} [colorStartB=Color()] - Color at start of life 2, randomized between start colors
|
|
3074
3404
|
* @param {Color} [colorEndA=Color(1,1,1,0)] - Color at end of life 1, randomized between end colors
|
|
@@ -3098,8 +3428,7 @@ class ParticleEmitter extends EngineObject
|
|
|
3098
3428
|
emitTime = 0,
|
|
3099
3429
|
emitRate = 100,
|
|
3100
3430
|
emitConeAngle = PI,
|
|
3101
|
-
|
|
3102
|
-
tileSize = tileSizeDefault,
|
|
3431
|
+
tileInfo,
|
|
3103
3432
|
colorStartA = new Color,
|
|
3104
3433
|
colorStartB = new Color,
|
|
3105
3434
|
colorEndA = new Color(1,1,1,0),
|
|
@@ -3122,7 +3451,7 @@ class ParticleEmitter extends EngineObject
|
|
|
3122
3451
|
localSpace
|
|
3123
3452
|
)
|
|
3124
3453
|
{
|
|
3125
|
-
super(pos, vec2(),
|
|
3454
|
+
super(pos, vec2(), tileInfo, angle, undefined, renderOrder);
|
|
3126
3455
|
|
|
3127
3456
|
// emitter settings
|
|
3128
3457
|
/** @property {Number} - World space size of the emitter (float for circle diameter, vec2 for rect) */
|
|
@@ -3221,7 +3550,7 @@ class ParticleEmitter extends EngineObject
|
|
|
3221
3550
|
angle += this.angle;
|
|
3222
3551
|
}
|
|
3223
3552
|
|
|
3224
|
-
const particle = new Particle(pos, this.
|
|
3553
|
+
const particle = new Particle(pos, this.tileInfo, this.tileSize, angle);
|
|
3225
3554
|
|
|
3226
3555
|
// randomness scales each paremeter by a percentage
|
|
3227
3556
|
const randomness = this.randomness;
|
|
@@ -3281,12 +3610,11 @@ class Particle extends EngineObject
|
|
|
3281
3610
|
/**
|
|
3282
3611
|
* Create a particle with the given settings
|
|
3283
3612
|
* @param {Vector2} position - World space position of the particle
|
|
3284
|
-
* @param {
|
|
3285
|
-
* @param {Vector2} [tileSize=tileSizeDefault] - Size of tile in source pixels
|
|
3613
|
+
* @param {TileInfo} [tileInfo] - Tile info to render particles (undefined is untextured)
|
|
3286
3614
|
* @param {Number} [angle=0] - Angle to rotate the particle
|
|
3287
3615
|
*/
|
|
3288
|
-
constructor(pos,
|
|
3289
|
-
{ super(pos, vec2(),
|
|
3616
|
+
constructor(pos, tileInfo, angle)
|
|
3617
|
+
{ super(pos, vec2(), tileInfo, angle); }
|
|
3290
3618
|
|
|
3291
3619
|
/** Render the particle, automatically called each frame, sorted by renderOrder */
|
|
3292
3620
|
render()
|
|
@@ -3320,14 +3648,17 @@ class Particle extends EngineObject
|
|
|
3320
3648
|
if (this.localSpaceEmitter)
|
|
3321
3649
|
velocity = velocity.rotate(-this.localSpaceEmitter.angle);
|
|
3322
3650
|
const speed = velocity.length();
|
|
3323
|
-
|
|
3324
|
-
|
|
3325
|
-
|
|
3326
|
-
|
|
3327
|
-
|
|
3651
|
+
if (speed)
|
|
3652
|
+
{
|
|
3653
|
+
const direction = velocity.scale(1/speed);
|
|
3654
|
+
const trailLength = speed * this.trailScale;
|
|
3655
|
+
size.y = max(size.x, trailLength);
|
|
3656
|
+
angle = direction.angle();
|
|
3657
|
+
drawTile(pos.add(direction.multiply(vec2(0,-trailLength/2))), size, this.tileInfo, color, angle, this.mirror);
|
|
3658
|
+
}
|
|
3328
3659
|
}
|
|
3329
3660
|
else
|
|
3330
|
-
drawTile(pos, size, this.
|
|
3661
|
+
drawTile(pos, size, this.tileInfo, color, angle, this.mirror);
|
|
3331
3662
|
this.additive && setBlendMode();
|
|
3332
3663
|
debugParticles && debugRect(pos, size, '#f005', 0, angle);
|
|
3333
3664
|
|
|
@@ -3349,7 +3680,7 @@ class Particle extends EngineObject
|
|
|
3349
3680
|
* @namespace Medals
|
|
3350
3681
|
*/
|
|
3351
3682
|
|
|
3352
|
-
|
|
3683
|
+
|
|
3353
3684
|
|
|
3354
3685
|
/** List of all medals
|
|
3355
3686
|
* @type {Array}
|
|
@@ -3374,7 +3705,7 @@ function medalsInit(saveName)
|
|
|
3374
3705
|
}
|
|
3375
3706
|
|
|
3376
3707
|
/**
|
|
3377
|
-
* Medal
|
|
3708
|
+
* Medal - Tracks an unlockable medal
|
|
3378
3709
|
* @example
|
|
3379
3710
|
* // create a medal
|
|
3380
3711
|
* const medal_example = new Medal(0, 'Example Medal', 'More info about the medal goes here.', '🎖️');
|
|
@@ -3387,7 +3718,7 @@ function medalsInit(saveName)
|
|
|
3387
3718
|
*/
|
|
3388
3719
|
class Medal
|
|
3389
3720
|
{
|
|
3390
|
-
/** Create
|
|
3721
|
+
/** Create a medal object and adds it to the list of medals
|
|
3391
3722
|
* @param {Number} id - The unique identifier of the medal
|
|
3392
3723
|
* @param {String} name - Name of the medal
|
|
3393
3724
|
* @param {String} [description] - Description of the medal
|
|
@@ -3499,33 +3830,33 @@ let newgrounds;
|
|
|
3499
3830
|
/** This can used to enable Newgrounds functionality
|
|
3500
3831
|
* @param {Number} app_id - The newgrounds App ID
|
|
3501
3832
|
* @param {String} [cipher] - The encryption Key (AES-128/Base64)
|
|
3833
|
+
* @param {Object} [cryptoJS] - An instance of CryptoJS, if there is a cipher
|
|
3502
3834
|
* @memberof Medals */
|
|
3503
|
-
function newgroundsInit(app_id, cipher
|
|
3835
|
+
function newgroundsInit(app_id, cipher, cryptoJS)
|
|
3836
|
+
{ newgrounds = new Newgrounds(app_id, cipher, cryptoJS); }
|
|
3504
3837
|
|
|
3505
3838
|
/**
|
|
3506
3839
|
* Newgrounds API wrapper object
|
|
3507
3840
|
* @example
|
|
3508
|
-
* // create a newgrounds object, replace the app id
|
|
3841
|
+
* // create a newgrounds object, replace the app id with your own
|
|
3509
3842
|
* const app_id = '53123:1ZuSTQ9l';
|
|
3510
|
-
*
|
|
3511
|
-
* newgrounds = new Newgrounds(app_id, cipher);
|
|
3843
|
+
* newgrounds = new Newgrounds(app_id);
|
|
3512
3844
|
*/
|
|
3513
3845
|
class Newgrounds
|
|
3514
3846
|
{
|
|
3515
3847
|
/** Create a newgrounds object
|
|
3516
3848
|
* @param {Number} app_id - The newgrounds App ID
|
|
3517
|
-
* @param {String} [cipher] - The encryption Key (AES-128/Base64)
|
|
3518
|
-
|
|
3849
|
+
* @param {String} [cipher] - The encryption Key (AES-128/Base64)
|
|
3850
|
+
* @param {Object} [cryptoJS] - An instance of CryptoJS, if there is a cipher */
|
|
3851
|
+
constructor(app_id, cipher, cryptoJS)
|
|
3519
3852
|
{
|
|
3520
|
-
ASSERT(!newgrounds && app_id);
|
|
3853
|
+
ASSERT(!newgrounds && app_id); // can only be one newgrounds object
|
|
3854
|
+
ASSERT(!cipher || cryptoJS); // must provide cryptojs if there is a cipher
|
|
3521
3855
|
|
|
3522
3856
|
this.app_id = app_id;
|
|
3523
3857
|
this.cipher = cipher;
|
|
3858
|
+
this.cryptoJS = cryptoJS;
|
|
3524
3859
|
this.host = location ? location.hostname : '';
|
|
3525
|
-
|
|
3526
|
-
// create an instance of CryptoJS for encrypted calls
|
|
3527
|
-
if (cipher)
|
|
3528
|
-
this.cryptoJS = this.CryptoJS();
|
|
3529
3860
|
|
|
3530
3861
|
// get session id from url search params
|
|
3531
3862
|
const url = new URL(location.href);
|
|
@@ -3629,38 +3960,6 @@ class Newgrounds
|
|
|
3629
3960
|
debugMedals && console.log(xmlHttp.responseText);
|
|
3630
3961
|
return xmlHttp.responseText && JSON.parse(xmlHttp.responseText);
|
|
3631
3962
|
}
|
|
3632
|
-
|
|
3633
|
-
CryptoJS()
|
|
3634
|
-
{
|
|
3635
|
-
///////////////////////////////////////////////////////////////////////////////
|
|
3636
|
-
// Crypto-JS - https://github.com/brix/crypto-js - MIT License
|
|
3637
|
-
//
|
|
3638
|
-
// [The MIT License (MIT)](http://opensource.org/licenses/MIT)
|
|
3639
|
-
//
|
|
3640
|
-
// Copyright (c) 2009-2013 Jeff Mott
|
|
3641
|
-
// Copyright (c) 2013-2016 Evan Vosberg
|
|
3642
|
-
//
|
|
3643
|
-
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
3644
|
-
// of this software and associated documentation files (the "Software"), to deal
|
|
3645
|
-
// in the Software without restriction, including without limitation the rights
|
|
3646
|
-
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
3647
|
-
// copies of the Software, and to permit persons to whom the Software is
|
|
3648
|
-
// furnished to do so, subject to the following conditions:
|
|
3649
|
-
//
|
|
3650
|
-
// The above copyright notice and this permission notice shall be included in
|
|
3651
|
-
// all copies or substantial portions of the Software.
|
|
3652
|
-
//
|
|
3653
|
-
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
3654
|
-
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
3655
|
-
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
3656
|
-
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
3657
|
-
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
3658
|
-
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
3659
|
-
// THE SOFTWARE.
|
|
3660
|
-
return eval(Function("[M='GBMGXz^oVYPPKKbB`agTXU|LxPc_ZBcMrZvCr~wyGfWrwk@ATqlqeTp^N?p{we}jIpEnB_sEr`l?YDkDhWhprc|Er|XETG?pTl`e}dIc[_N~}fzRycIfpW{HTolvoPB_FMe_eH~BTMx]yyOhv?biWPCGc]kABencBhgERHGf{OL`Dj`c^sh@canhy[secghiyotcdOWgO{tJIE^JtdGQRNSCrwKYciZOa]Y@tcRATYKzv|sXpboHcbCBf`}SKeXPFM|RiJsSNaIb]QPc[D]Jy_O^XkOVTZep`ONmntLL`Qz~UupHBX_Ia~WX]yTRJIxG`ioZ{fefLJFhdyYoyLPvqgH?b`[TMnTwwfzDXhfM?rKs^aFr|nyBdPmVHTtAjXoYUloEziWDCw_suyYT~lSMksI~ZNCS[Bex~j]Vz?kx`gdYSEMCsHpjbyxQvw|XxX_^nQYue{sBzVWQKYndtYQMWRef{bOHSfQhiNdtR{o?cUAHQAABThwHPT}F{VvFmgN`E@FiFYS`UJmpQNM`X|tPKHlccT}z}k{sACHL?Rt@MkWplxO`ASgh?hBsuuP|xD~LSH~KBlRs]t|l|_tQAroDRqWS^SEr[sYdPB}TAROtW{mIkE|dWOuLgLmJrucGLpebrAFKWjikTUzS|j}M}szasKOmrjy[?hpwnEfX[jGpLt@^v_eNwSQHNwtOtDgWD{rk|UgASs@mziIXrsHN_|hZuxXlPJOsA^^?QY^yGoCBx{ekLuZzRqQZdsNSx@ezDAn{XNj@fRXIwrDX?{ZQHwTEfu@GhxDOykqts|n{jOeZ@c`dvTY?e^]ATvWpb?SVyg]GC?SlzteilZJAL]mlhLjYZazY__qcVFYvt@|bIQnSno@OXyt]OulzkWqH`rYFWrwGs`v|~XeTsIssLrbmHZCYHiJrX}eEzSssH}]l]IhPQhPoQ}rCXLyhFIT[clhzYOvyHqigxmjz`phKUU^TPf[GRAIhNqSOdayFP@FmKmuIzMOeoqdpxyCOwCthcLq?n`L`tLIBboNn~uXeFcPE{C~mC`h]jUUUQe^`UqvzCutYCgct|SBrAeiYQW?X~KzCz}guXbsUw?pLsg@hDArw?KeJD[BN?GD@wgFWCiHq@Ypp_QKFixEKWqRp]oJFuVIEvjDcTFu~Zz]a{IcXhWuIdMQjJ]lwmGQ|]g~c]Hl]pl`Pd^?loIcsoNir_kikBYyg?NarXZEGYspt_vLBIoj}LI[uBFvm}tbqvC|xyR~a{kob|HlctZslTGtPDhBKsNsoZPuH`U`Fqg{gKnGSHVLJ^O`zmNgMn~{rsQuoymw^JY?iUBvw_~mMr|GrPHTERS[MiNpY[Mm{ggHpzRaJaoFomtdaQ_?xuTRm}@KjU~RtPsAdxa|uHmy}n^i||FVL[eQAPrWfLm^ndczgF~Nk~aplQvTUpHvnTya]kOenZlLAQIm{lPl@CCTchvCF[fI{^zPkeYZTiamoEcKmBMfZhk_j_~Fjp|wPVZlkh_nHu]@tP|hS@^G^PdsQ~f[RqgTDqezxNFcaO}HZhb|MMiNSYSAnQWCDJukT~e|OTgc}sf[cnr?fyzTa|EwEtRG|I~|IO}O]S|rp]CQ}}DWhSjC_|z|oY|FYl@WkCOoPuWuqr{fJu?Brs^_EBI[@_OCKs}?]O`jnDiXBvaIWhhMAQDNb{U`bqVR}oqVAvR@AZHEBY@depD]OLh`kf^UsHhzKT}CS}HQKy}Q~AeMydXPQztWSSzDnghULQgMAmbWIZ|lWWeEXrE^EeNoZApooEmrXe{NAnoDf`m}UNlRdqQ@jOc~HLOMWs]IDqJHYoMziEedGBPOxOb?[X`KxkFRg@`mgFYnP{hSaxwZfBQqTm}_?RSEaQga]w[vxc]hMne}VfSlqUeMo_iqmd`ilnJXnhdj^EEFifvZyxYFRf^VaqBhLyrGlk~qowqzHOBlOwtx?i{m~`n^G?Yxzxux}b{LSlx]dS~thO^lYE}bzKmUEzwW^{rPGhbEov[Plv??xtyKJshbG`KuO?hjBdS@Ru}iGpvFXJRrvOlrKN?`I_n_tplk}kgwSXuKylXbRQ]]?a|{xiT[li?k]CJpwy^o@ebyGQrPfF`aszGKp]baIx~H?ElETtFh]dz[OjGl@C?]VDhr}OE@V]wLTc[WErXacM{We`F|utKKjgllAxvsVYBZ@HcuMgLboFHVZmi}eIXAIFhS@A@FGRbjeoJWZ_NKd^oEH`qgy`q[Tq{x?LRP|GfBFFJV|fgZs`MLbpPYUdIV^]mD@FG]pYAT^A^RNCcXVrPsgk{jTrAIQPs_`mD}rOqAZA[}RETFz]WkXFTz_m{N@{W@_fPKZLT`@aIqf|L^Mb|crNqZ{BVsijzpGPEKQQZGlApDn`ruH}cvF|iXcNqK}cxe_U~HRnKV}sCYb`D~oGvwG[Ca|UaybXea~DdD~LiIbGRxJ_VGheI{ika}KC[OZJLn^IBkPrQj_EuoFwZ}DpoBRcK]Q}?EmTv~i_Tul{bky?Iit~tgS|o}JL_VYcCQdjeJ_MfaA`FgCgc[Ii|CBHwq~nbJeYTK{e`CNstKfTKPzw{jdhp|qsZyP_FcugxCFNpKitlR~vUrx^NrSVsSTaEgnxZTmKc`R|lGJeX}ccKLsQZQhsFkeFd|ckHIVTlGMg`~uPwuHRJS_CPuN_ogXe{Ba}dO_UBhuNXby|h?JlgBIqMKx^_u{molgL[W_iavNQuOq?ap]PGB`clAicnl@k~pA?MWHEZ{HuTLsCpOxxrKlBh]FyMjLdFl|nMIvTHyGAlPogqfZ?PlvlFJvYnDQd}R@uAhtJmDfe|iJqdkYr}r@mEjjIetDl_I`TELfoR|qTBu@Tic[BaXjP?dCS~MUK[HPRI}OUOwAaf|_}HZzrwXvbnNgltjTwkBE~MztTQhtRSWoQHajMoVyBBA`kdgK~h`o[J`dm~pm]tk@i`[F~F]DBlJKklrkR]SNw@{aG~Vhl`KINsQkOy?WhcqUMTGDOM_]bUjVd|Yh_KUCCgIJ|LDIGZCPls{RzbVWVLEhHvWBzKq|^N?DyJB|__aCUjoEgsARki}j@DQXS`RNU|DJ^a~d{sh_Iu{ONcUtSrGWW@cvUjefHHi}eSSGrNtO?cTPBShLqzwMVjWQQCCFB^culBjZHEK_{dO~Q`YhJYFn]jq~XSnG@[lQr]eKrjXpG~L^h~tDgEma^AUFThlaR{xyuP@[^VFwXSeUbVetufa@dX]CLyAnDV@Bs[DnpeghJw^?UIana}r_CKGDySoRudklbgio}kIDpA@McDoPK?iYcG?_zOmnWfJp}a[JLR[stXMo?_^Ng[whQlrDbrawZeSZ~SJstIObdDSfAA{MV}?gNunLOnbMv_~KFQUAjIMj^GkoGxuYtYbGDImEYiwEMyTpMxN_LSnSMdl{bg@dtAnAMvhDTBR_FxoQgANniRqxd`pWv@rFJ|mWNWmh[GMJz_Nq`BIN@KsjMPASXORcdHjf~rJfgZYe_uulzqM_KdPlMsuvU^YJuLtofPhGonVOQxCMuXliNvJIaoC?hSxcxKVVxWlNs^ENDvCtSmO~WxI[itnjs^RDvI@KqG}YekaSbTaB]ki]XM@[ZnDAP~@|BzLRgOzmjmPkRE@_sobkT|SszXK[rZN?F]Z_u}Yue^[BZgLtR}FHzWyxWEX^wXC]MJmiVbQuBzkgRcKGUhOvUc_bga|Tx`KEM`JWEgTpFYVeXLCm|mctZR@uKTDeUONPozBeIkrY`cz]]~WPGMUf`MNUGHDbxZuO{gmsKYkAGRPqjc|_FtblEOwy}dnwCHo]PJhN~JoteaJ?dmYZeB^Xd?X^pOKDbOMF@Ugg^hETLdhwlA}PL@_ur|o{VZosP?ntJ_kG][g{Zq`Tu]dzQlSWiKfnxDnk}KOzp~tdFstMobmy[oPYjyOtUzMWdjcNSUAjRuqhLS@AwB^{BFnqjCmmlk?jpn}TksS{KcKkDboXiwK]qMVjm~V`LgWhjS^nLGwfhAYrjDSBL_{cRus~{?xar_xqPlArrYFd?pHKdMEZzzjJpfC?Hv}mAuIDkyBxFpxhstTx`IO{rp}XGuQ]VtbHerlRc_LFGWK[XluFcNGUtDYMZny[M^nVKVeMllQI[xtvwQnXFlWYqxZZFp_|]^oWX[{pOMpxXxvkbyJA[DrPzwD|LW|QcV{Nw~U^dgguSpG]ClmO@j_TENIGjPWwgdVbHganhM?ema|dBaqla|WBd`poj~klxaasKxGG^xbWquAl~_lKWxUkDFagMnE{zHug{b`A~IYcQYBF_E}wiA}K@yxWHrZ{[d~|ARsYsjeNWzkMs~IOqqp[yzDE|WFrivsidTcnbHFRoW@XpAV`lv_zj?B~tPCppRjgbbDTALeFaOf?VcjnKTQMLyp{NwdylHCqmo?oelhjWuXj~}{fpuX`fra?GNkDiChYgVSh{R[BgF~eQa^WVz}ATI_CpY?g_diae]|ijH`TyNIF}|D_xpmBq_JpKih{Ba|sWzhnAoyraiDvk`h{qbBfsylBGmRH}DRPdryEsSaKS~tIaeF[s]I~xxHVrcNe@Jjxa@jlhZueLQqHh_]twVMqG_EGuwyab{nxOF?`HCle}nBZzlTQjkLmoXbXhOtBglFoMz?eqre`HiE@vNwBulglmQjj]DB@pPkPUgA^sjOAUNdSu_`oAzar?n?eMnw{{hYmslYi[TnlJD'",...']charCodeAtUinyxpf',"for(;e<10359;c[e++]=p-=128,A=A?p-A&&A:p==34&&p)for(p=1;p<128;y=f.map((n,x)=>(U=r[n]*2+1,U=Math.log(U/(h-U)),t-=a[x]*U,U/500)),t=~-h/(1+Math.exp(t))|1,i=o%h<t,o=o%h+(i?t:h-t)*(o>>17)-!i*t,f.map((n,x)=>(U=r[n]+=(i*h/2-r[n]<<13)/((C[n]+=C[n]<5)+1/20)>>13,a[x]+=y[x]*(i-t/h))),p=p*2+i)for(f='010202103203210431053105410642065206541'.split(t=0).map((n,x)=>(U=0,[...n].map((n,x)=>(U=U*997+(c[e-n]|0)|0)),h*32-1&U*997+p+!!A*129)*12+x);o<h*32;o=o*64|M.charCodeAt(d++)&63);for(C=String.fromCharCode(...c);r=/[\0-#?@\\\\~]/.exec(C);)with(C.split(r))C=join(shift());return C")([],[],1<<17,[0,0,0,0,0,0,0,0,0,0,0,0],new Uint16Array(51e6).fill(1<<15),new Uint8Array(51e6),0,0,0,0));
|
|
3661
|
-
// end of Crypto-JS
|
|
3662
|
-
///////////////////////////////////////////////////////////////////////////////
|
|
3663
|
-
}
|
|
3664
3963
|
}
|
|
3665
3964
|
/**
|
|
3666
3965
|
* LittleJS WebGL Interface
|
|
@@ -3670,10 +3969,11 @@ class Newgrounds
|
|
|
3670
3969
|
* - Can be disabled with glEnable to revert to 2D canvas rendering
|
|
3671
3970
|
* - Batches sprite rendering on GPU for incredibly fast performance
|
|
3672
3971
|
* - Sprite transform math is done in the shader where possible
|
|
3972
|
+
* - Supports shadertoy style post processing shaders
|
|
3673
3973
|
* @namespace WebGL
|
|
3674
3974
|
*/
|
|
3675
3975
|
|
|
3676
|
-
|
|
3976
|
+
|
|
3677
3977
|
|
|
3678
3978
|
/** The WebGL canvas which appears above the main canvas and below the overlay canvas
|
|
3679
3979
|
* @type {HTMLCanvasElement}
|
|
@@ -3685,45 +3985,42 @@ let glCanvas;
|
|
|
3685
3985
|
* @memberof WebGL */
|
|
3686
3986
|
let glContext;
|
|
3687
3987
|
|
|
3688
|
-
/** Main tile sheet texture automatically loaded by engine
|
|
3689
|
-
* @type {WebGLTexture}
|
|
3690
|
-
* @memberof WebGL */
|
|
3691
|
-
let glTileTexture;
|
|
3692
|
-
|
|
3693
3988
|
// WebGL internal variables not exposed to documentation
|
|
3694
3989
|
let glActiveTexture, glShader, glArrayBuffer, glPositionData, glColorData, glBatchCount, glBatchAdditive, glAdditive;
|
|
3695
3990
|
|
|
3696
3991
|
///////////////////////////////////////////////////////////////////////////////
|
|
3697
3992
|
|
|
3698
|
-
//
|
|
3993
|
+
// Initalize WebGL, called automatically by the engine
|
|
3699
3994
|
function glInit()
|
|
3700
3995
|
{
|
|
3701
|
-
// create the canvas and
|
|
3996
|
+
// create the canvas and textures
|
|
3702
3997
|
glCanvas = document.createElement('canvas');
|
|
3703
|
-
glContext = glCanvas.getContext('
|
|
3704
|
-
glTileTexture = glCreateTexture(tileImage);
|
|
3998
|
+
glContext = glCanvas.getContext('webgl2');
|
|
3705
3999
|
|
|
3706
4000
|
// some browsers are much faster without copying the gl buffer so we just overlay it instead
|
|
3707
4001
|
glOverlay && document.body.appendChild(glCanvas);
|
|
3708
4002
|
|
|
3709
4003
|
// setup vertex and fragment shaders
|
|
3710
4004
|
glShader = glCreateProgram(
|
|
4005
|
+
'#version 300 es\n' + // specify GLSL ES version
|
|
3711
4006
|
'precision highp float;'+ // use highp for better accuracy
|
|
3712
4007
|
'uniform mat4 m;'+ // transform matrix
|
|
3713
|
-
'
|
|
3714
|
-
'
|
|
3715
|
-
'
|
|
4008
|
+
'in vec2 p,t;'+ // position, uv
|
|
4009
|
+
'in vec4 c,a;'+ // color, additiveColor
|
|
4010
|
+
'out vec4 v,d,e;'+ // return uv, color, additiveColor
|
|
3716
4011
|
'void main(){'+ // shader entry point
|
|
3717
4012
|
'gl_Position=m*vec4(p,1,1);'+ // transform position
|
|
3718
4013
|
'v=vec4(t,p);d=c;e=a;'+ // pass stuff to fragment shader
|
|
3719
4014
|
'}' // end of shader
|
|
3720
4015
|
,
|
|
3721
|
-
'
|
|
3722
|
-
'
|
|
3723
|
-
'
|
|
3724
|
-
'
|
|
3725
|
-
'
|
|
3726
|
-
'
|
|
4016
|
+
'#version 300 es\n' + // specify GLSL ES version
|
|
4017
|
+
'precision highp float;'+ // use highp for better accuracy
|
|
4018
|
+
'in vec4 v,d,e;'+ // uv, color, additiveColor
|
|
4019
|
+
'uniform sampler2D s;'+ // texture
|
|
4020
|
+
'out vec4 c;'+ // out color
|
|
4021
|
+
'void main(){'+ // shader entry point
|
|
4022
|
+
'c=texture(s,v.xy)*d+e;'+ // modulate texture by color plus additive
|
|
4023
|
+
'}' // end of shader
|
|
3727
4024
|
);
|
|
3728
4025
|
|
|
3729
4026
|
// init buffers
|
|
@@ -3734,25 +4031,68 @@ function glInit()
|
|
|
3734
4031
|
glBatchCount = 0;
|
|
3735
4032
|
}
|
|
3736
4033
|
|
|
4034
|
+
// Setup render each frame, called automatically by engine
|
|
4035
|
+
function glPreRender()
|
|
4036
|
+
{
|
|
4037
|
+
// clear and set to same size as main canvas
|
|
4038
|
+
glContext.viewport(0, 0, glCanvas.width = mainCanvas.width, glCanvas.height = mainCanvas.height);
|
|
4039
|
+
glContext.clear(gl_COLOR_BUFFER_BIT);
|
|
4040
|
+
|
|
4041
|
+
// set up the shader
|
|
4042
|
+
glContext.useProgram(glShader);
|
|
4043
|
+
glContext.activeTexture(gl_TEXTURE0);
|
|
4044
|
+
glContext.bindTexture(gl_TEXTURE_2D, glActiveTexture = textureInfos[0].glTexture);
|
|
4045
|
+
glContext.bindBuffer(gl_ARRAY_BUFFER, glArrayBuffer);
|
|
4046
|
+
glContext.bufferData(gl_ARRAY_BUFFER, gl_VERTEX_BUFFER_SIZE, gl_DYNAMIC_DRAW);
|
|
4047
|
+
glSetBlendMode();
|
|
4048
|
+
|
|
4049
|
+
// set vertex attributes
|
|
4050
|
+
let offset = 0;
|
|
4051
|
+
const initVertexAttribArray = (name, type, typeSize, size, normalize=0)=>
|
|
4052
|
+
{
|
|
4053
|
+
const location = glContext.getAttribLocation(glShader, name);
|
|
4054
|
+
glContext.enableVertexAttribArray(location);
|
|
4055
|
+
glContext.vertexAttribPointer(location, size, type, normalize, gl_VERTEX_BYTE_STRIDE, offset);
|
|
4056
|
+
offset += size*typeSize;
|
|
4057
|
+
}
|
|
4058
|
+
initVertexAttribArray('p', gl_FLOAT, 4, 2); // position
|
|
4059
|
+
initVertexAttribArray('t', gl_FLOAT, 4, 2); // texture coords
|
|
4060
|
+
initVertexAttribArray('c', gl_UNSIGNED_BYTE, 1, 4, 1); // color
|
|
4061
|
+
initVertexAttribArray('a', gl_UNSIGNED_BYTE, 1, 4, 1); // additiveColor
|
|
4062
|
+
|
|
4063
|
+
// build the transform matrix
|
|
4064
|
+
const sx = 2 * cameraScale / mainCanvas.width;
|
|
4065
|
+
const sy = 2 * cameraScale / mainCanvas.height;
|
|
4066
|
+
glContext.uniformMatrix4fv(glContext.getUniformLocation(glShader, 'm'), 0,
|
|
4067
|
+
new Float32Array([
|
|
4068
|
+
sx, 0, 0, 0,
|
|
4069
|
+
0, sy, 0, 0,
|
|
4070
|
+
1, 1, -1, 1,
|
|
4071
|
+
-1-sx*cameraPos.x, -1-sy*cameraPos.y, 0, 0
|
|
4072
|
+
])
|
|
4073
|
+
);
|
|
4074
|
+
}
|
|
4075
|
+
|
|
3737
4076
|
/** Set the WebGl blend mode, normally you should call setBlendMode instead
|
|
3738
4077
|
* @param {Boolean} [additive=0]
|
|
3739
4078
|
* @memberof WebGL */
|
|
3740
|
-
function glSetBlendMode(additive)
|
|
4079
|
+
function glSetBlendMode(additive=0)
|
|
3741
4080
|
{
|
|
3742
4081
|
// setup blending
|
|
3743
4082
|
glAdditive = additive;
|
|
3744
4083
|
}
|
|
3745
4084
|
|
|
3746
|
-
/** Set the WebGl texture,
|
|
4085
|
+
/** Set the WebGl texture, called automatically if using multiple textures
|
|
3747
4086
|
* - This may also flush the gl buffer resulting in more draw calls and worse performance
|
|
3748
|
-
* @param {WebGLTexture}
|
|
4087
|
+
* @param {WebGLTexture} texture
|
|
3749
4088
|
* @memberof WebGL */
|
|
3750
|
-
function glSetTexture(texture
|
|
4089
|
+
function glSetTexture(texture)
|
|
3751
4090
|
{
|
|
3752
4091
|
// must flush cache with the old texture to set a new one
|
|
3753
|
-
if (texture
|
|
3754
|
-
|
|
4092
|
+
if (texture == glActiveTexture)
|
|
4093
|
+
return;
|
|
3755
4094
|
|
|
4095
|
+
glFlush();
|
|
3756
4096
|
glContext.bindTexture(gl_TEXTURE_2D, glActiveTexture = texture);
|
|
3757
4097
|
}
|
|
3758
4098
|
|
|
@@ -3813,48 +4153,6 @@ function glCreateTexture(image)
|
|
|
3813
4153
|
return texture;
|
|
3814
4154
|
}
|
|
3815
4155
|
|
|
3816
|
-
// called automatically by engine before render
|
|
3817
|
-
function glPreRender()
|
|
3818
|
-
{
|
|
3819
|
-
// clear and set to same size as main canvas
|
|
3820
|
-
glContext.viewport(0, 0, glCanvas.width = mainCanvas.width, glCanvas.height = mainCanvas.height);
|
|
3821
|
-
glContext.clear(gl_COLOR_BUFFER_BIT);
|
|
3822
|
-
|
|
3823
|
-
// set up the shader
|
|
3824
|
-
glContext.useProgram(glShader);
|
|
3825
|
-
glContext.activeTexture(gl_TEXTURE0);
|
|
3826
|
-
glContext.bindTexture(gl_TEXTURE_2D, glActiveTexture = glTileTexture);
|
|
3827
|
-
glContext.bindBuffer(gl_ARRAY_BUFFER, glArrayBuffer);
|
|
3828
|
-
glContext.bufferData(gl_ARRAY_BUFFER, gl_VERTEX_BUFFER_SIZE, gl_DYNAMIC_DRAW);
|
|
3829
|
-
glSetBlendMode();
|
|
3830
|
-
|
|
3831
|
-
// set vertex attributes
|
|
3832
|
-
let offset = 0;
|
|
3833
|
-
const initVertexAttribArray = (name, type, typeSize, size, normalize=0)=>
|
|
3834
|
-
{
|
|
3835
|
-
const location = glContext.getAttribLocation(glShader, name);
|
|
3836
|
-
glContext.enableVertexAttribArray(location);
|
|
3837
|
-
glContext.vertexAttribPointer(location, size, type, normalize, gl_VERTEX_BYTE_STRIDE, offset);
|
|
3838
|
-
offset += size*typeSize;
|
|
3839
|
-
}
|
|
3840
|
-
initVertexAttribArray('p', gl_FLOAT, 4, 2); // position
|
|
3841
|
-
initVertexAttribArray('t', gl_FLOAT, 4, 2); // texture coords
|
|
3842
|
-
initVertexAttribArray('c', gl_UNSIGNED_BYTE, 1, 4, 1); // color
|
|
3843
|
-
initVertexAttribArray('a', gl_UNSIGNED_BYTE, 1, 4, 1); // additiveColor
|
|
3844
|
-
|
|
3845
|
-
// build the transform matrix
|
|
3846
|
-
const sx = 2 * cameraScale / mainCanvas.width;
|
|
3847
|
-
const sy = 2 * cameraScale / mainCanvas.height;
|
|
3848
|
-
glContext.uniformMatrix4fv(glContext.getUniformLocation(glShader, 'm'), 0,
|
|
3849
|
-
new Float32Array([
|
|
3850
|
-
sx, 0, 0, 0,
|
|
3851
|
-
0, sy, 0, 0,
|
|
3852
|
-
1, 1, -1, 1,
|
|
3853
|
-
-1-sx*cameraPos.x, -1-sy*cameraPos.y, 0, 0
|
|
3854
|
-
])
|
|
3855
|
-
);
|
|
3856
|
-
}
|
|
3857
|
-
|
|
3858
4156
|
/** Draw all sprites and clear out the buffer, called automatically by the system whenever necessary
|
|
3859
4157
|
* @memberof WebGL */
|
|
3860
4158
|
function glFlush()
|
|
@@ -3973,25 +4271,28 @@ function glInitPostProcess(shaderCode, includeOverlay)
|
|
|
3973
4271
|
{
|
|
3974
4272
|
ASSERT(!glPostShader); // can only have 1 post effects shader
|
|
3975
4273
|
|
|
3976
|
-
if (!shaderCode) // default shader
|
|
3977
|
-
shaderCode = 'void mainImage(out vec4 c,vec2 p){c=
|
|
4274
|
+
if (!shaderCode) // default shader pass through
|
|
4275
|
+
shaderCode = 'void mainImage(out vec4 c,vec2 p){c=texture(iChannel0,p/iResolution.xy);}';
|
|
3978
4276
|
|
|
3979
4277
|
// create the shader
|
|
3980
4278
|
glPostShader = glCreateProgram(
|
|
4279
|
+
'#version 300 es\n' + // specify GLSL ES version
|
|
3981
4280
|
'precision highp float;'+ // use highp for better accuracy
|
|
3982
|
-
'
|
|
4281
|
+
'in vec2 p;'+ // position
|
|
3983
4282
|
'void main(){'+ // shader entry point
|
|
3984
4283
|
'gl_Position=vec4(p,1,1);'+ // set position
|
|
3985
4284
|
'}' // end of shader
|
|
3986
4285
|
,
|
|
4286
|
+
'#version 300 es\n' + // specify GLSL ES version
|
|
3987
4287
|
'precision highp float;'+ // use highp for better accuracy
|
|
3988
4288
|
'uniform sampler2D iChannel0;'+ // input texture
|
|
3989
4289
|
'uniform vec3 iResolution;'+ // size of output texture
|
|
3990
4290
|
'uniform float iTime;'+ // time passed
|
|
4291
|
+
'out vec4 c;'+ // out color
|
|
3991
4292
|
'\n' + shaderCode + '\n'+ // insert custom shader code
|
|
3992
4293
|
'void main(){'+ // shader entry point
|
|
3993
|
-
'mainImage(
|
|
3994
|
-
'
|
|
4294
|
+
'mainImage(c,gl_FragCoord.xy);'+ // call post process function
|
|
4295
|
+
'c.a=1.;'+ // always use full alpha
|
|
3995
4296
|
'}' // end of shader
|
|
3996
4297
|
);
|
|
3997
4298
|
|
|
@@ -4064,7 +4365,6 @@ gl_ONE_MINUS_SRC_ALPHA = 771,
|
|
|
4064
4365
|
gl_BLEND = 3042,
|
|
4065
4366
|
gl_TEXTURE_2D = 3553,
|
|
4066
4367
|
gl_UNSIGNED_BYTE = 5121,
|
|
4067
|
-
gl_BYTE = 5120,
|
|
4068
4368
|
gl_FLOAT = 5126,
|
|
4069
4369
|
gl_RGBA = 6408,
|
|
4070
4370
|
gl_NEAREST = 9728,
|
|
@@ -4076,7 +4376,6 @@ gl_TEXTURE_WRAP_T = 10243,
|
|
|
4076
4376
|
gl_COLOR_BUFFER_BIT = 16384,
|
|
4077
4377
|
gl_CLAMP_TO_EDGE = 33071,
|
|
4078
4378
|
gl_TEXTURE0 = 33984,
|
|
4079
|
-
gl_TEXTURE1 = 33985,
|
|
4080
4379
|
gl_ARRAY_BUFFER = 34962,
|
|
4081
4380
|
gl_STATIC_DRAW = 35044,
|
|
4082
4381
|
gl_DYNAMIC_DRAW = 35048,
|
|
@@ -4087,7 +4386,6 @@ gl_LINK_STATUS = 35714,
|
|
|
4087
4386
|
gl_UNPACK_FLIP_Y_WEBGL = 37440,
|
|
4088
4387
|
|
|
4089
4388
|
// constants for batch rendering
|
|
4090
|
-
gl_VERTICES_PER_QUAD = 6,
|
|
4091
4389
|
gl_INDICIES_PER_VERT = 6,
|
|
4092
4390
|
gl_MAX_BATCH = 1e5,
|
|
4093
4391
|
gl_VERTEX_BYTE_STRIDE = (4 * 2) * 2 + (4) * 2, // vec2 * 2 + (char * 4) * 2
|
|
@@ -4112,7 +4410,7 @@ gl_VERTEX_BUFFER_SIZE = gl_MAX_BATCH * gl_VERTEX_BYTE_STRIDE;
|
|
|
4112
4410
|
* @namespace Engine
|
|
4113
4411
|
*/
|
|
4114
4412
|
|
|
4115
|
-
|
|
4413
|
+
|
|
4116
4414
|
|
|
4117
4415
|
/** Name of engine
|
|
4118
4416
|
* @type {String}
|
|
@@ -4124,7 +4422,7 @@ const engineName = 'LittleJS';
|
|
|
4124
4422
|
* @type {String}
|
|
4125
4423
|
* @default
|
|
4126
4424
|
* @memberof Engine */
|
|
4127
|
-
const engineVersion = '1.
|
|
4425
|
+
const engineVersion = '1.8.1';
|
|
4128
4426
|
|
|
4129
4427
|
/** Frames per second to update objects
|
|
4130
4428
|
* @type {Number}
|
|
@@ -4174,6 +4472,9 @@ let paused = 0;
|
|
|
4174
4472
|
* @memberof Engine */
|
|
4175
4473
|
function setPaused(_paused) { paused = _paused; }
|
|
4176
4474
|
|
|
4475
|
+
// Frame time tracking
|
|
4476
|
+
let frameTimeLastMS = 0, frameTimeBufferMS = 0, averageFPS = 0;
|
|
4477
|
+
|
|
4177
4478
|
///////////////////////////////////////////////////////////////////////////////
|
|
4178
4479
|
|
|
4179
4480
|
/** Start up LittleJS engine with your callback functions
|
|
@@ -4182,49 +4483,13 @@ function setPaused(_paused) { paused = _paused; }
|
|
|
4182
4483
|
* @param {Function} gameUpdatePost - Called after physics and objects are updated, setup camera and prepare for render
|
|
4183
4484
|
* @param {Function} gameRender - Called before objects are rendered, draw any background effects that appear behind objects
|
|
4184
4485
|
* @param {Function} gameRenderPost - Called after objects are rendered, draw effects or hud that appear above all objects
|
|
4185
|
-
* @param {String} [
|
|
4486
|
+
* @param {String} [imageSources='tiles.png'] - Image to load
|
|
4186
4487
|
* @memberof Engine */
|
|
4187
|
-
function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRenderPost,
|
|
4488
|
+
function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRenderPost, imageSources=['tiles.png'])
|
|
4188
4489
|
{
|
|
4189
|
-
//
|
|
4190
|
-
tileImage.onerror = tileImage.onload = ()=>
|
|
4191
|
-
{
|
|
4192
|
-
// save tile image info
|
|
4193
|
-
tileImageFixBleed = vec2(tileFixBleedScale).divide(tileImageSize = vec2(tileImage.width, tileImage.height));
|
|
4194
|
-
debug && (tileImage.onload=()=>ASSERT(1)); // tile sheet can not reloaded
|
|
4195
|
-
|
|
4196
|
-
// setup html
|
|
4197
|
-
const styleBody = 'margin:0;overflow:hidden;' + // fill the window
|
|
4198
|
-
'background:#000;' + // set background color
|
|
4199
|
-
'touch-action:none;' + // prevent mobile pinch to resize
|
|
4200
|
-
'user-select:none;' + // prevent mobile hold to select
|
|
4201
|
-
'-webkit-user-select:none'; // compatibility for ios
|
|
4202
|
-
document.body.style = styleBody;
|
|
4203
|
-
document.body.appendChild(mainCanvas = document.createElement('canvas'));
|
|
4204
|
-
mainContext = mainCanvas.getContext('2d');
|
|
4205
|
-
|
|
4206
|
-
// init stuff and start engine
|
|
4207
|
-
debugInit();
|
|
4208
|
-
glEnable && glInit();
|
|
4209
|
-
|
|
4210
|
-
// create overlay canvas for hud to appear above gl canvas
|
|
4211
|
-
document.body.appendChild(overlayCanvas = document.createElement('canvas'));
|
|
4212
|
-
overlayContext = overlayCanvas.getContext('2d');
|
|
4213
|
-
|
|
4214
|
-
// set canvas style
|
|
4215
|
-
const styleCanvas = 'position:absolute;' +
|
|
4216
|
-
'top:50%;left:50%;transform:translate(-50%,-50%);' + // center the canvas
|
|
4217
|
-
(canvasPixelated?'image-rendering:pixelated':''); // set pixelated rendering
|
|
4218
|
-
(glCanvas||mainCanvas).style = mainCanvas.style = overlayCanvas.style = styleCanvas;
|
|
4219
|
-
|
|
4220
|
-
gameInit();
|
|
4221
|
-
engineUpdate();
|
|
4222
|
-
};
|
|
4490
|
+
ASSERT(Array.isArray(imageSources)); // pass in images as array
|
|
4223
4491
|
|
|
4224
|
-
//
|
|
4225
|
-
let frameTimeLastMS = 0, frameTimeBufferMS = 0, averageFPS = 0;
|
|
4226
|
-
|
|
4227
|
-
// main update loop
|
|
4492
|
+
// internal update loop for engine
|
|
4228
4493
|
function engineUpdate(frameTimeMS=0)
|
|
4229
4494
|
{
|
|
4230
4495
|
// update time keeping
|
|
@@ -4255,9 +4520,9 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
4255
4520
|
}
|
|
4256
4521
|
else
|
|
4257
4522
|
{
|
|
4258
|
-
|
|
4259
|
-
|
|
4260
|
-
|
|
4523
|
+
// clear canvas and set size to same as window
|
|
4524
|
+
mainCanvas.width = min(innerWidth, canvasMaxSize.x);
|
|
4525
|
+
mainCanvas.height = min(innerHeight, canvasMaxSize.y);
|
|
4261
4526
|
}
|
|
4262
4527
|
|
|
4263
4528
|
// clear overlay canvas and set size
|
|
@@ -4289,6 +4554,9 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
4289
4554
|
// update multiple frames if necessary in case of slow framerate
|
|
4290
4555
|
for (;frameTimeBufferMS >= 0; frameTimeBufferMS -= 1e3 / frameRate)
|
|
4291
4556
|
{
|
|
4557
|
+
// increment frame and update time
|
|
4558
|
+
time = frame++ / frameRate;
|
|
4559
|
+
|
|
4292
4560
|
// update game and objects
|
|
4293
4561
|
inputUpdate();
|
|
4294
4562
|
gameUpdate();
|
|
@@ -4336,8 +4604,51 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
4336
4604
|
requestAnimationFrame(engineUpdate);
|
|
4337
4605
|
}
|
|
4338
4606
|
|
|
4339
|
-
//
|
|
4340
|
-
|
|
4607
|
+
// setup html
|
|
4608
|
+
const styleBody =
|
|
4609
|
+
'margin:0;overflow:hidden;' + // fill the window
|
|
4610
|
+
'background:#000;' + // set background color
|
|
4611
|
+
'touch-action:none;' + // prevent mobile pinch to resize
|
|
4612
|
+
'user-select:none;' + // prevent mobile hold to select
|
|
4613
|
+
'-webkit-user-select:none;' + // compatibility for ios
|
|
4614
|
+
'-webkit-touch-callout:none'; // compatibility for ios
|
|
4615
|
+
document.body.style = styleBody;
|
|
4616
|
+
document.body.appendChild(mainCanvas = document.createElement('canvas'));
|
|
4617
|
+
mainContext = mainCanvas.getContext('2d');
|
|
4618
|
+
|
|
4619
|
+
// init stuff and start engine
|
|
4620
|
+
debugInit();
|
|
4621
|
+
glEnable && glInit();
|
|
4622
|
+
|
|
4623
|
+
// create overlay canvas for hud to appear above gl canvas
|
|
4624
|
+
document.body.appendChild(overlayCanvas = document.createElement('canvas'));
|
|
4625
|
+
overlayContext = overlayCanvas.getContext('2d');
|
|
4626
|
+
|
|
4627
|
+
// set canvas style
|
|
4628
|
+
const styleCanvas =
|
|
4629
|
+
'position:absolute;' + // position
|
|
4630
|
+
'top:50%;left:50%;transform:translate(-50%,-50%);' + // center
|
|
4631
|
+
(canvasPixelated?'image-rendering:pixelated':''); // pixelated rendering
|
|
4632
|
+
(glCanvas||mainCanvas).style = mainCanvas.style = overlayCanvas.style = styleCanvas;
|
|
4633
|
+
|
|
4634
|
+
// load all of the images
|
|
4635
|
+
Promise.all(imageSources.map((src, textureIndex)=>
|
|
4636
|
+
new Promise((resolve, reject)=>
|
|
4637
|
+
{
|
|
4638
|
+
const image = new Image;
|
|
4639
|
+
image.onerror = image.onload = ()=>
|
|
4640
|
+
{
|
|
4641
|
+
textureInfos[textureIndex] = new TextureInfo(image);
|
|
4642
|
+
resolve();
|
|
4643
|
+
}
|
|
4644
|
+
image.src = src;
|
|
4645
|
+
})
|
|
4646
|
+
)).then(()=>
|
|
4647
|
+
{
|
|
4648
|
+
// start the engine
|
|
4649
|
+
gameInit();
|
|
4650
|
+
engineUpdate();
|
|
4651
|
+
});
|
|
4341
4652
|
}
|
|
4342
4653
|
|
|
4343
4654
|
// Called automatically by engine to setup render system
|
|
@@ -4375,9 +4686,6 @@ function engineObjectsUpdate()
|
|
|
4375
4686
|
|
|
4376
4687
|
// remove destroyed objects
|
|
4377
4688
|
engineObjects = engineObjects.filter(o=>!o.destroyed);
|
|
4378
|
-
|
|
4379
|
-
// increment frame and update time
|
|
4380
|
-
time = ++frame / frameRate;
|
|
4381
4689
|
}
|
|
4382
4690
|
|
|
4383
4691
|
/** Destroy and remove all objects
|