littlejsengine 1.6.0 → 1.6.6
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 +138 -34
- package/build/littlejs.d.ts +104 -92
- package/build/littlejs.esm.js +414 -342
- package/build/littlejs.esm.min.js +1 -1
- package/build/littlejs.js +365 -296
- package/build/littlejs.min.js +1 -1
- package/build/littlejs.release.js +359 -291
- package/examples/breakout/game.js +5 -0
- package/examples/breakout/gameObjects.js +41 -48
- package/examples/breakout/index.html +3 -3
- package/examples/breakoutTutorial/README.md +514 -0
- package/examples/breakoutTutorial/game.js +181 -0
- package/examples/breakoutTutorial/images/1.png +0 -0
- package/examples/breakoutTutorial/images/10.png +0 -0
- package/examples/breakoutTutorial/images/11.png +0 -0
- package/examples/breakoutTutorial/images/2.png +0 -0
- package/examples/breakoutTutorial/images/3.png +0 -0
- package/examples/breakoutTutorial/images/4.png +0 -0
- package/examples/breakoutTutorial/images/5.png +0 -0
- package/examples/breakoutTutorial/images/6.png +0 -0
- package/examples/breakoutTutorial/images/7.png +0 -0
- package/examples/breakoutTutorial/images/8.png +0 -0
- package/examples/breakoutTutorial/images/9.png +0 -0
- package/examples/breakoutTutorial/index.html +10 -0
- package/examples/empty/game.js +10 -0
- package/examples/favicon.png +0 -0
- package/examples/module/index.html +1 -1
- package/examples/particles/index.html +1 -1
- package/examples/platformer/gameLevel.js +1 -1
- package/examples/platformer/gamePlayer.js +1 -1
- package/examples/platformer/index.html +6 -6
- package/examples/puzzle/game.js +1 -1
- package/examples/puzzle/index.html +2 -2
- package/examples/starter/build.bat +3 -2
- package/examples/starter/game.js +9 -9
- package/examples/starter/index.html +13 -13
- package/examples/stress/index.html +1 -1
- package/examples/typescript/game.js +89 -89
- package/examples/typescript/game.ts +10 -10
- package/examples/typescript/index.html +1 -1
- package/package.json +4 -4
- package/src/engine.js +29 -31
- package/src/engineAudio.js +53 -29
- package/src/engineBuild.bat +3 -1
- package/src/engineDebug.js +25 -24
- package/src/engineDraw.js +43 -39
- package/src/engineExport.js +49 -46
- package/src/engineInput.js +83 -74
- package/src/engineMedals.js +31 -8
- package/src/engineObject.js +25 -25
- package/src/engineParticles.js +3 -6
- package/src/engineRelease.js +19 -19
- package/src/engineSettings.js +4 -4
- package/src/engineTileLayer.js +18 -14
- package/src/engineUtilities.js +43 -34
- package/src/engineWebGL.js +8 -8
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
*/
|
|
1
|
+
/**
|
|
2
|
+
* LittleJS - Release Build
|
|
3
|
+
* MIT License - Copyright 2021 Frank Force
|
|
4
|
+
* - This file is used for release builds in place of engineDebug.js
|
|
5
|
+
* - Debug functionality is disabled to reduce size and increase performance
|
|
6
|
+
*/
|
|
8
7
|
|
|
9
8
|
'use strict';
|
|
10
9
|
|
|
11
10
|
let showWatermark = 0;
|
|
12
11
|
let godMode = 0;
|
|
12
|
+
let debugKeyCode = 0;
|
|
13
13
|
const debug = 0;
|
|
14
14
|
const debugOverlay = 0;
|
|
15
15
|
const debugPhysics = 0;
|
|
@@ -19,24 +19,24 @@ const debugGamepads = 0;
|
|
|
19
19
|
const debugMedals = 0;
|
|
20
20
|
|
|
21
21
|
// debug commands are automatically removed from the final build
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
22
|
+
function ASSERT (){}
|
|
23
|
+
function debugInit (){}
|
|
24
|
+
function debugUpdate (){}
|
|
25
|
+
function debugRender (){}
|
|
26
|
+
function debugRect (){}
|
|
27
|
+
function debugCircle (){}
|
|
28
|
+
function debugPoint (){}
|
|
29
|
+
function debugLine (){}
|
|
30
|
+
function debugAABB (){}
|
|
31
|
+
function debugText (){}
|
|
32
|
+
function debugClear (){}
|
|
33
|
+
function debugSaveCanvas (){}
|
|
34
34
|
/**
|
|
35
35
|
* LittleJS Utility Classes and Functions
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
36
|
+
* - General purpose math library
|
|
37
|
+
* - Vector2 - fast, simple, easy 2D vector class
|
|
38
|
+
* - Color - holds a rgba color with some math functions
|
|
39
|
+
* - Timer - tracks time automatically
|
|
40
40
|
* @namespace Utilities
|
|
41
41
|
*/
|
|
42
42
|
|
|
@@ -52,34 +52,34 @@ const PI = Math.PI;
|
|
|
52
52
|
* @param {Number} value
|
|
53
53
|
* @return {Number}
|
|
54
54
|
* @memberof Utilities */
|
|
55
|
-
|
|
55
|
+
function abs(a) { return a < 0 ? -a : a; }
|
|
56
56
|
|
|
57
57
|
/** Returns lowest of two values passed in
|
|
58
58
|
* @param {Number} valueA
|
|
59
59
|
* @param {Number} valueB
|
|
60
60
|
* @return {Number}
|
|
61
61
|
* @memberof Utilities */
|
|
62
|
-
|
|
62
|
+
function min(a, b) { return a < b ? a : b; }
|
|
63
63
|
|
|
64
64
|
/** Returns highest of two values passed in
|
|
65
65
|
* @param {Number} valueA
|
|
66
66
|
* @param {Number} valueB
|
|
67
67
|
* @return {Number}
|
|
68
68
|
* @memberof Utilities */
|
|
69
|
-
|
|
69
|
+
function max(a, b) { return a > b ? a : b; }
|
|
70
70
|
|
|
71
71
|
/** Returns the sign of value passed in (also returns 1 if 0)
|
|
72
72
|
* @param {Number} value
|
|
73
73
|
* @return {Number}
|
|
74
74
|
* @memberof Utilities */
|
|
75
|
-
|
|
75
|
+
function sign(a) { return a < 0 ? -1 : 1; }
|
|
76
76
|
|
|
77
77
|
/** Returns first parm modulo the second param, but adjusted so negative numbers work as expected
|
|
78
78
|
* @param {Number} dividend
|
|
79
79
|
* @param {Number} [divisor=1]
|
|
80
80
|
* @return {Number}
|
|
81
81
|
* @memberof Utilities */
|
|
82
|
-
|
|
82
|
+
function mod(a, b=1) { return ((a % b) + b) % b; }
|
|
83
83
|
|
|
84
84
|
/** Clamps the value beween max and min
|
|
85
85
|
* @param {Number} value
|
|
@@ -87,7 +87,8 @@ const mod = (a, b=1)=> ((a % b) + b) % b;
|
|
|
87
87
|
* @param {Number} [max=1]
|
|
88
88
|
* @return {Number}
|
|
89
89
|
* @memberof Utilities */
|
|
90
|
-
|
|
90
|
+
function clamp(v, min=0, max=1)
|
|
91
|
+
{ return v < min ? min : v > max ? max : v; }
|
|
91
92
|
|
|
92
93
|
/** Returns what percentage the value is between max and min
|
|
93
94
|
* @param {Number} value
|
|
@@ -95,7 +96,8 @@ const clamp = (v, min=0, max=1)=> v < min ? min : v > max ? max : v;
|
|
|
95
96
|
* @param {Number} [max=1]
|
|
96
97
|
* @return {Number}
|
|
97
98
|
* @memberof Utilities */
|
|
98
|
-
|
|
99
|
+
function percent(v, min=0, max=1)
|
|
100
|
+
{ return max-min ? clamp((v-min) / (max-min)) : 0; }
|
|
99
101
|
|
|
100
102
|
/** Linearly interpolates the percent value between max and min
|
|
101
103
|
* @param {Number} percent
|
|
@@ -103,19 +105,19 @@ const percent = (v, min=0, max=1)=> max-min ? clamp((v-min) / (max-min)) : 0;
|
|
|
103
105
|
* @param {Number} [max=1]
|
|
104
106
|
* @return {Number}
|
|
105
107
|
* @memberof Utilities */
|
|
106
|
-
|
|
108
|
+
function lerp(p, min=0, max=1){ return min + clamp(p) * (max-min); }
|
|
107
109
|
|
|
108
110
|
/** Applies smoothstep function to the percentage value
|
|
109
111
|
* @param {Number} value
|
|
110
112
|
* @return {Number}
|
|
111
113
|
* @memberof Utilities */
|
|
112
|
-
|
|
114
|
+
function smoothStep(p) { return p * p * (3 - 2 * p); }
|
|
113
115
|
|
|
114
116
|
/** Returns the nearest power of two not less then the value
|
|
115
117
|
* @param {Number} value
|
|
116
118
|
* @return {Number}
|
|
117
119
|
* @memberof Utilities */
|
|
118
|
-
|
|
120
|
+
function nearestPowerOfTwo(v) { return 2**Math.ceil(Math.log2(v)); }
|
|
119
121
|
|
|
120
122
|
/** Returns true if two axis aligned bounding boxes are overlapping
|
|
121
123
|
* @param {Vector2} pointA - Center of box A
|
|
@@ -124,7 +126,8 @@ const nearestPowerOfTwo = (v)=> 2**Math.ceil(Math.log2(v));
|
|
|
124
126
|
* @param {Vector2} [sizeB] - Size of box B
|
|
125
127
|
* @return {Boolean} - True if overlapping
|
|
126
128
|
* @memberof Utilities */
|
|
127
|
-
|
|
129
|
+
function isOverlapping(pA, sA, pB, sB)
|
|
130
|
+
{ return abs(pA.x - pB.x)*2 < sA.x + sB.x && abs(pA.y - pB.y)*2 < sA.y + sB.y; }
|
|
128
131
|
|
|
129
132
|
/** Returns an oscillating wave between 0 and amplitude with frequency of 1 Hz by default
|
|
130
133
|
* @param {Number} [frequency=1] - Frequency of the wave in Hz
|
|
@@ -132,13 +135,14 @@ const isOverlapping = (pA, sA, pB, sB)=> abs(pA.x - pB.x)*2 < sA.x + sB.x && abs
|
|
|
132
135
|
* @param {Number} [t=time] - Value to use for time of the wave
|
|
133
136
|
* @return {Number} - Value waving between 0 and amplitude
|
|
134
137
|
* @memberof Utilities */
|
|
135
|
-
|
|
138
|
+
function wave(frequency=1, amplitude=1, t=time)
|
|
139
|
+
{ return amplitude/2 * (1 - Math.cos(t*frequency*2*PI)); }
|
|
136
140
|
|
|
137
141
|
/** Formats seconds to mm:ss style for display purposes
|
|
138
142
|
* @param {Number} t - time in seconds
|
|
139
143
|
* @return {String}
|
|
140
144
|
* @memberof Utilities */
|
|
141
|
-
|
|
145
|
+
function formatTime(t) { return (t/60|0) + ':' + (t%60<10?'0':'') + (t%60|0); }
|
|
142
146
|
|
|
143
147
|
///////////////////////////////////////////////////////////////////////////////
|
|
144
148
|
|
|
@@ -150,32 +154,33 @@ const formatTime = (t)=> (t/60|0) + ':' + (t%60<10?'0':'') + (t%60|0);
|
|
|
150
154
|
* @param {Number} [valueB=0]
|
|
151
155
|
* @return {Number}
|
|
152
156
|
* @memberof Random */
|
|
153
|
-
|
|
157
|
+
function rand(a=1, b=0) { return b + (a-b)*Math.random(); }
|
|
154
158
|
|
|
155
159
|
/** Returns a floored random value the two values passed in
|
|
156
160
|
* @param {Number} [valueA=1]
|
|
157
161
|
* @param {Number} [valueB=0]
|
|
158
162
|
* @return {Number}
|
|
159
163
|
* @memberof Random */
|
|
160
|
-
|
|
164
|
+
function randInt(a=1, b=0) { return rand(a,b)|0; }
|
|
161
165
|
|
|
162
166
|
/** Randomly returns either -1 or 1
|
|
163
167
|
* @return {Number}
|
|
164
168
|
* @memberof Random */
|
|
165
|
-
|
|
169
|
+
function randSign() { return randInt(2) * 2 - 1; }
|
|
166
170
|
|
|
167
171
|
/** Returns a random Vector2 within a circular shape
|
|
168
172
|
* @param {Number} [radius=1]
|
|
169
173
|
* @param {Number} [minRadius=0]
|
|
170
174
|
* @return {Vector2}
|
|
171
175
|
* @memberof Random */
|
|
172
|
-
|
|
176
|
+
function randInCircle(radius=1, minRadius=0)
|
|
177
|
+
{ return radius > 0 ? randVector(radius * rand(minRadius / radius, 1)**.5) : new Vector2; }
|
|
173
178
|
|
|
174
179
|
/** Returns a random Vector2 with the passed in length
|
|
175
180
|
* @param {Number} [length=1]
|
|
176
181
|
* @return {Vector2}
|
|
177
182
|
* @memberof Random */
|
|
178
|
-
|
|
183
|
+
function randVector(length=1) { return new Vector2().setAngle(rand(2*PI), length); }
|
|
179
184
|
|
|
180
185
|
/** Returns a random color between the two passed in colors, combine components if linear
|
|
181
186
|
* @param {Color} [colorA=Color()]
|
|
@@ -183,8 +188,8 @@ const randVector = (length=1)=> new Vector2().setAngle(rand(2*PI), length);
|
|
|
183
188
|
* @param {Boolean} [linear]
|
|
184
189
|
* @return {Color}
|
|
185
190
|
* @memberof Random */
|
|
186
|
-
|
|
187
|
-
|
|
191
|
+
function randColor(cA = new Color, cB = new Color(0,0,0,1), linear)
|
|
192
|
+
{ return linear ? cA.lerp(cB, rand()) : new Color(rand(cA.r,cB.r),rand(cA.g,cB.g),rand(cA.b,cB.b),rand(cA.a,cB.a)); }
|
|
188
193
|
|
|
189
194
|
/** Seed used by the randSeeded function
|
|
190
195
|
* @type {Number}
|
|
@@ -195,16 +200,19 @@ let randSeed = 1;
|
|
|
195
200
|
/** Set seed used by the randSeeded function, should not be 0
|
|
196
201
|
* @param {Number} seed
|
|
197
202
|
* @memberof Random */
|
|
198
|
-
|
|
203
|
+
function setRandSeed(seed) { randSeed = seed; }
|
|
199
204
|
|
|
200
205
|
/** Returns a seeded random value between the two values passed in using randSeed
|
|
201
206
|
* @param {Number} [valueA=1]
|
|
202
207
|
* @param {Number} [valueB=0]
|
|
203
208
|
* @return {Number}
|
|
204
209
|
* @memberof Random */
|
|
205
|
-
|
|
210
|
+
function randSeeded(a=1, b=0)
|
|
206
211
|
{
|
|
207
|
-
|
|
212
|
+
// xorshift algorithm
|
|
213
|
+
randSeed ^= randSeed << 13;
|
|
214
|
+
randSeed ^= randSeed >>> 17;
|
|
215
|
+
randSeed ^= randSeed << 5;
|
|
208
216
|
return b + (a-b) * abs(randSeed % 1e9) / 1e9;
|
|
209
217
|
}
|
|
210
218
|
|
|
@@ -222,7 +230,8 @@ const randSeeded = (a=1, b=0)=>
|
|
|
222
230
|
* b = vec2(); // set b to (0, 0)
|
|
223
231
|
* @memberof Utilities
|
|
224
232
|
*/
|
|
225
|
-
|
|
233
|
+
function vec2(x=0, y)
|
|
234
|
+
{ return x.x == undefined ? new Vector2(x, y == undefined? x : y) : new Vector2(x.x, x.y); }
|
|
226
235
|
|
|
227
236
|
/**
|
|
228
237
|
* Check if object is a valid Vector2
|
|
@@ -230,11 +239,11 @@ const vec2 = (x=0, y)=> x.x == undefined ? new Vector2(x, y == undefined? x : y)
|
|
|
230
239
|
* @return {Boolean}
|
|
231
240
|
* @memberof Utilities
|
|
232
241
|
*/
|
|
233
|
-
|
|
242
|
+
function isVector2(v) { return !isNaN(v.x) && !isNaN(v.y); }
|
|
234
243
|
|
|
235
244
|
/**
|
|
236
245
|
* 2D Vector object with vector math library
|
|
237
|
-
*
|
|
246
|
+
* - Functions do not change this so they can be chained together
|
|
238
247
|
* @example
|
|
239
248
|
* let a = new Vector2(2, 3); // vector with coordinates (2, 3)
|
|
240
249
|
* let b = new Vector2; // vector with coordinates (0, 0)
|
|
@@ -381,7 +390,7 @@ class Vector2
|
|
|
381
390
|
* @return {Color}
|
|
382
391
|
* @memberof Utilities
|
|
383
392
|
*/
|
|
384
|
-
|
|
393
|
+
function rgb(r, g, b, a) { return new Color(r, g, b, a); }
|
|
385
394
|
|
|
386
395
|
/**
|
|
387
396
|
* Create a color object with HSLA values
|
|
@@ -392,7 +401,7 @@ const colorRGBA = (r, g, b, a)=> new Color(r, g, b, a);
|
|
|
392
401
|
* @return {Color}
|
|
393
402
|
* @memberof Utilities
|
|
394
403
|
*/
|
|
395
|
-
|
|
404
|
+
function hsl(h, s, l, a) { return new Color().setHSLA(h, s, l, a); }
|
|
396
405
|
|
|
397
406
|
/**
|
|
398
407
|
* Color object (red, green, blue, alpha) with some helpful functions
|
|
@@ -400,8 +409,8 @@ const colorHSLA = (h, s, l, a)=> new Color().setHSLA(h, s, l, a);
|
|
|
400
409
|
* let a = new Color; // white
|
|
401
410
|
* let b = new Color(1, 0, 0); // red
|
|
402
411
|
* let c = new Color(0, 0, 0, 0); // transparent black
|
|
403
|
-
* let d =
|
|
404
|
-
* let e =
|
|
412
|
+
* let d = RGB(0, 0, 1); // blue using rgb color
|
|
413
|
+
* let e = HSL(.3, 1, .5); // green using hsl color
|
|
405
414
|
*/
|
|
406
415
|
class Color
|
|
407
416
|
{
|
|
@@ -651,11 +660,11 @@ let canvasMaxSize = vec2(1920, 1200);
|
|
|
651
660
|
* @memberof Settings */
|
|
652
661
|
let canvasFixedSize = vec2();
|
|
653
662
|
|
|
654
|
-
/** Disables
|
|
663
|
+
/** Disables filtering for crisper pixel art if true
|
|
655
664
|
* @type {Boolean}
|
|
656
665
|
* @default
|
|
657
666
|
* @memberof Settings */
|
|
658
|
-
let
|
|
667
|
+
let canvasPixelated = 1;
|
|
659
668
|
|
|
660
669
|
/** Default font used for text rendering
|
|
661
670
|
* @type {String}
|
|
@@ -772,8 +781,8 @@ let gamepadDirectionEmulateStick = 1;
|
|
|
772
781
|
let inputWASDEmulateDirection = 1;
|
|
773
782
|
|
|
774
783
|
/** True if touch gamepad should appear on mobile devices
|
|
775
|
-
*
|
|
776
|
-
*
|
|
784
|
+
* - Supports left analog stick, 4 face buttons and start button (button 9)
|
|
785
|
+
* - Must be set by end of gameInit to be activated
|
|
777
786
|
* @type {Boolean}
|
|
778
787
|
* @default 0
|
|
779
788
|
* @memberof Settings */
|
|
@@ -862,27 +871,23 @@ let medalDisplayIconSize = 50;
|
|
|
862
871
|
* @default 0
|
|
863
872
|
* @memberof Settings */
|
|
864
873
|
let medalsPreventUnlock;
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
*/
|
|
883
|
-
|
|
884
|
-
/**
|
|
885
|
-
* LittleJS Engine Globals
|
|
874
|
+
/**
|
|
875
|
+
* LittleJS - The Tiny JavaScript Game Engine That Can!
|
|
876
|
+
* MIT License - Copyright 2021 Frank Force
|
|
877
|
+
*
|
|
878
|
+
* Engine Features
|
|
879
|
+
* - Object oriented system with base class engine object
|
|
880
|
+
* - Base class object handles update, physics, collision, rendering, etc
|
|
881
|
+
* - Engine helper classes and functions like Vector2, Color, and Timer
|
|
882
|
+
* - Super fast rendering system for tile sheets
|
|
883
|
+
* - Sound effects audio with zzfx and music with zzfxm
|
|
884
|
+
* - Input processing system with gamepad and touchscreen support
|
|
885
|
+
* - Tile layer rendering and collision system
|
|
886
|
+
* - Particle effect system
|
|
887
|
+
* - Medal system tracks and displays achievements
|
|
888
|
+
* - Debug tools and debug rendering system
|
|
889
|
+
* - Post processing effects
|
|
890
|
+
* - Call engineInit() to start it up!
|
|
886
891
|
* @namespace Engine
|
|
887
892
|
*/
|
|
888
893
|
|
|
@@ -898,7 +903,7 @@ const engineName = 'LittleJS';
|
|
|
898
903
|
* @type {String}
|
|
899
904
|
* @default
|
|
900
905
|
* @memberof Engine */
|
|
901
|
-
const engineVersion = '1.6.
|
|
906
|
+
const engineVersion = '1.6.6';
|
|
902
907
|
|
|
903
908
|
/** Frames per second to update objects
|
|
904
909
|
* @type {Number}
|
|
@@ -968,10 +973,11 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
968
973
|
debug && (tileImage.onload=()=>ASSERT(1)); // tile sheet can not reloaded
|
|
969
974
|
|
|
970
975
|
// setup html
|
|
971
|
-
const styleBody = 'margin:0;overflow:hidden;
|
|
972
|
-
';
|
|
973
|
-
'
|
|
974
|
-
'
|
|
976
|
+
const styleBody = 'margin:0;overflow:hidden;' + // fill the window
|
|
977
|
+
'background:#000;' + // set background color
|
|
978
|
+
'touch-action:none;' + // prevent mobile pinch to resize
|
|
979
|
+
'user-select:none;' + // prevent mobile hold to select
|
|
980
|
+
'-webkit-user-select:none'; // compatibility for ios
|
|
975
981
|
document.body.style = styleBody;
|
|
976
982
|
document.body.appendChild(mainCanvas = document.createElement('canvas'));
|
|
977
983
|
mainContext = mainCanvas.getContext('2d');
|
|
@@ -984,12 +990,13 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
984
990
|
document.body.appendChild(overlayCanvas = document.createElement('canvas'));
|
|
985
991
|
overlayContext = overlayCanvas.getContext('2d');
|
|
986
992
|
|
|
987
|
-
// set canvas style
|
|
988
|
-
const styleCanvas = 'position:absolute;
|
|
993
|
+
// set canvas style
|
|
994
|
+
const styleCanvas = 'position:absolute;' +
|
|
995
|
+
'top:50%;left:50%;transform:translate(-50%,-50%);' + // center the canvas
|
|
996
|
+
(canvasPixelated?'image-rendering:pixelated':''); // set pixelated rendering
|
|
989
997
|
(glCanvas||mainCanvas).style = mainCanvas.style = overlayCanvas.style = styleCanvas;
|
|
990
998
|
|
|
991
999
|
gameInit();
|
|
992
|
-
touchGamepadCreate();
|
|
993
1000
|
engineUpdate();
|
|
994
1001
|
};
|
|
995
1002
|
|
|
@@ -1119,7 +1126,7 @@ function enginePreRender()
|
|
|
1119
1126
|
mainCanvasSize = vec2(mainCanvas.width, mainCanvas.height);
|
|
1120
1127
|
|
|
1121
1128
|
// disable smoothing for pixel art
|
|
1122
|
-
mainContext.imageSmoothingEnabled = !
|
|
1129
|
+
mainContext.imageSmoothingEnabled = !canvasPixelated;
|
|
1123
1130
|
|
|
1124
1131
|
// setup gl rendering if enabled
|
|
1125
1132
|
glEnable && glPreRender();
|
|
@@ -1133,7 +1140,7 @@ function engineObjectsUpdate()
|
|
|
1133
1140
|
engineObjectsCollide = engineObjects.filter(o=>o.collideSolidObjects);
|
|
1134
1141
|
|
|
1135
1142
|
// recursive object update
|
|
1136
|
-
|
|
1143
|
+
function updateObject(o)
|
|
1137
1144
|
{
|
|
1138
1145
|
if (!o.destroyed)
|
|
1139
1146
|
{
|
|
@@ -1186,32 +1193,32 @@ function engineObjectsCallback(pos, size, callbackFunction, objects=engineObject
|
|
|
1186
1193
|
pos.distanceSquared(o.pos) < sizeSquared && callbackFunction(o);
|
|
1187
1194
|
}
|
|
1188
1195
|
}
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
*/
|
|
1196
|
+
/**
|
|
1197
|
+
* LittleJS Object System
|
|
1198
|
+
*/
|
|
1192
1199
|
|
|
1193
1200
|
'use strict';
|
|
1194
1201
|
|
|
1195
1202
|
/**
|
|
1196
1203
|
* LittleJS Object Base Object Class
|
|
1197
|
-
*
|
|
1198
|
-
*
|
|
1199
|
-
*
|
|
1200
|
-
*
|
|
1201
|
-
*
|
|
1202
|
-
*
|
|
1203
|
-
*
|
|
1204
|
-
*
|
|
1205
|
-
*
|
|
1206
|
-
*
|
|
1207
|
-
*
|
|
1208
|
-
*
|
|
1209
|
-
*
|
|
1210
|
-
*
|
|
1211
|
-
*
|
|
1212
|
-
*
|
|
1213
|
-
*
|
|
1214
|
-
*
|
|
1204
|
+
* - Base object class used by the engine
|
|
1205
|
+
* - Automatically adds self to object list
|
|
1206
|
+
* - Will be updated and rendered each frame
|
|
1207
|
+
* - Renders as a sprite from a tilesheet by default
|
|
1208
|
+
* - Can have color and addtive color applied
|
|
1209
|
+
* - 2d Physics and collision system
|
|
1210
|
+
* - Sorted by renderOrder
|
|
1211
|
+
* - Objects can have children attached
|
|
1212
|
+
* - Parents are updated before children, and set child transform
|
|
1213
|
+
* - Call destroy() to get rid of objects
|
|
1214
|
+
*
|
|
1215
|
+
* The physics system used by objects is simple and fast with some caveats...
|
|
1216
|
+
* - Collision uses the axis aligned size, the object's rotation angle is only for rendering
|
|
1217
|
+
* - Objects are guaranteed to not intersect tile collision from physics
|
|
1218
|
+
* - If an object starts or is moved inside tile collision, it will not collide with that tile
|
|
1219
|
+
* - Collision for objects can be set to be solid to block other objects
|
|
1220
|
+
* - Objects may get pushed into overlapping other solid objects, if so they will push away
|
|
1221
|
+
* - Solid objects are more performance intensive and should be used sparingly
|
|
1215
1222
|
* @example
|
|
1216
1223
|
* // create an engine object, normally you would first extend the class with your own
|
|
1217
1224
|
* const pos = vec2(2,3);
|
|
@@ -1300,6 +1307,7 @@ class EngineObject
|
|
|
1300
1307
|
this.velocity.y += gravity * this.gravityScale;
|
|
1301
1308
|
this.pos.x += this.velocity.x *= this.damping;
|
|
1302
1309
|
this.pos.y += this.velocity.y *= this.damping;
|
|
1310
|
+
this.angle += this.angleVelocity *= this.angleDamping;
|
|
1303
1311
|
|
|
1304
1312
|
// physics sanity checks
|
|
1305
1313
|
ASSERT(this.angleDamping >= 0 && this.angleDamping <= 1);
|
|
@@ -1356,6 +1364,7 @@ class EngineObject
|
|
|
1356
1364
|
const smallStepUp = (oldPos.y - o.pos.y)*2 > sizeBoth.y + gravity; // prefer to push up if small delta
|
|
1357
1365
|
const isBlockedX = abs(oldPos.y - o.pos.y)*2 < sizeBoth.y;
|
|
1358
1366
|
const isBlockedY = abs(oldPos.x - o.pos.x)*2 < sizeBoth.x;
|
|
1367
|
+
const elasticity = max(this.elasticity, o.elasticity);
|
|
1359
1368
|
|
|
1360
1369
|
if (smallStepUp | isBlockedY | !isBlockedX) // resolve y collision
|
|
1361
1370
|
{
|
|
@@ -1368,7 +1377,7 @@ class EngineObject
|
|
|
1368
1377
|
this.groundObject = o;
|
|
1369
1378
|
|
|
1370
1379
|
// bounce if other object is fixed or grounded
|
|
1371
|
-
this.velocity.y *= -
|
|
1380
|
+
this.velocity.y *= -elasticity;
|
|
1372
1381
|
}
|
|
1373
1382
|
else if (o.mass)
|
|
1374
1383
|
{
|
|
@@ -1382,7 +1391,6 @@ class EngineObject
|
|
|
1382
1391
|
+ this.velocity.y * 2 * this.mass / (this.mass + o.mass);
|
|
1383
1392
|
|
|
1384
1393
|
// lerp betwen elastic or inelastic based on elasticity
|
|
1385
|
-
const elasticity = max(this.elasticity, o.elasticity);
|
|
1386
1394
|
this.velocity.y = lerp(elasticity, inelastic, elastic0);
|
|
1387
1395
|
o.velocity.y = lerp(elasticity, inelastic, elastic1);
|
|
1388
1396
|
}
|
|
@@ -1403,12 +1411,11 @@ class EngineObject
|
|
|
1403
1411
|
+ this.velocity.x * 2 * this.mass / (this.mass + o.mass);
|
|
1404
1412
|
|
|
1405
1413
|
// lerp betwen elastic or inelastic based on elasticity
|
|
1406
|
-
const elasticity = max(this.elasticity, o.elasticity);
|
|
1407
1414
|
this.velocity.x = lerp(elasticity, inelastic, elastic0);
|
|
1408
1415
|
o.velocity.x = lerp(elasticity, inelastic, elastic1);
|
|
1409
1416
|
}
|
|
1410
1417
|
else // bounce if other object is fixed
|
|
1411
|
-
this.velocity.x *= -
|
|
1418
|
+
this.velocity.x *= -elasticity;
|
|
1412
1419
|
}
|
|
1413
1420
|
debugOverlay && debugPhysics && debugAABB(this.pos, this.size, o.pos, o.size, '#f0f');
|
|
1414
1421
|
}
|
|
@@ -1564,23 +1571,23 @@ class EngineObject
|
|
|
1564
1571
|
}
|
|
1565
1572
|
/**
|
|
1566
1573
|
* LittleJS Drawing System
|
|
1567
|
-
*
|
|
1568
|
-
*
|
|
1569
|
-
*
|
|
1570
|
-
*
|
|
1571
|
-
*
|
|
1572
|
-
*
|
|
1573
|
-
*
|
|
1574
|
-
*
|
|
1575
|
-
*
|
|
1576
|
-
*
|
|
1577
|
-
*
|
|
1578
|
-
*
|
|
1579
|
-
*
|
|
1580
|
-
*
|
|
1581
|
-
*
|
|
1582
|
-
*
|
|
1583
|
-
*
|
|
1574
|
+
* - Hybrid with both Canvas2D and WebGL available
|
|
1575
|
+
* - Super fast tile sheet rendering with WebGL
|
|
1576
|
+
* - Can apply rotation, mirror, color and additive color
|
|
1577
|
+
* - Many useful utility functions
|
|
1578
|
+
*
|
|
1579
|
+
* LittleJS uses a hybrid rendering solution with the best of both Canvas2D and WebGL.
|
|
1580
|
+
* There are 3 canvas/contexts available to draw to...
|
|
1581
|
+
* mainCanvas - 2D background canvas, non WebGL stuff like tile layers are drawn here.
|
|
1582
|
+
* glCanvas - Used by the accelerated WebGL batch rendering system.
|
|
1583
|
+
* overlayCanvas - Another 2D canvas that appears on top of the other 2 canvases.
|
|
1584
|
+
*
|
|
1585
|
+
* The WebGL rendering system is very fast with some caveats...
|
|
1586
|
+
* - The default setup supports only 1 tile sheet, to support more call glCreateTexture and glSetTexture
|
|
1587
|
+
* - Switching blend modes (additive) or textures causes another draw call which is expensive in excess
|
|
1588
|
+
* - Group additive rendering together using renderOrder to mitigate this issue
|
|
1589
|
+
*
|
|
1590
|
+
* The LittleJS rendering solution is intentionally simple, feel free to adjust it for your needs!
|
|
1584
1591
|
* @namespace Draw
|
|
1585
1592
|
*/
|
|
1586
1593
|
|
|
@@ -1624,7 +1631,7 @@ let tileImageSize, tileImageFixBleed, drawCount;
|
|
|
1624
1631
|
* @param {Vector2} screenPos
|
|
1625
1632
|
* @return {Vector2}
|
|
1626
1633
|
* @memberof Draw */
|
|
1627
|
-
|
|
1634
|
+
function screenToWorld(screenPos)
|
|
1628
1635
|
{
|
|
1629
1636
|
ASSERT(mainCanvasSize.x && mainCanvasSize.y, 'mainCanvasSize is invalid');
|
|
1630
1637
|
return screenPos.add(vec2(.5)).subtract(mainCanvasSize.scale(.5)).multiply(vec2(1/cameraScale,-1/cameraScale)).add(cameraPos);
|
|
@@ -1635,7 +1642,7 @@ const screenToWorld = (screenPos)=>
|
|
|
1635
1642
|
* @param {Vector2} worldPos
|
|
1636
1643
|
* @return {Vector2}
|
|
1637
1644
|
* @memberof Draw */
|
|
1638
|
-
|
|
1645
|
+
function worldToScreen(worldPos)
|
|
1639
1646
|
{
|
|
1640
1647
|
ASSERT(mainCanvasSize.x && mainCanvasSize.y, 'mainCanvasSize is invalid');
|
|
1641
1648
|
return worldPos.subtract(cameraPos).multiply(vec2(cameraScale,-cameraScale)).add(mainCanvasSize.scale(.5)).subtract(vec2(.5));
|
|
@@ -1790,6 +1797,23 @@ function setBlendMode(additive, useWebGL=glEnable)
|
|
|
1790
1797
|
mainContext.globalCompositeOperation = additive ? 'lighter' : 'source-over';
|
|
1791
1798
|
}
|
|
1792
1799
|
|
|
1800
|
+
/** Draw text on overlay canvas in world space
|
|
1801
|
+
* Automatically splits new lines into rows
|
|
1802
|
+
* @param {String} text
|
|
1803
|
+
* @param {Vector2} pos
|
|
1804
|
+
* @param {Number} [size=1]
|
|
1805
|
+
* @param {Color} [color=Color()]
|
|
1806
|
+
* @param {Number} [lineWidth=0]
|
|
1807
|
+
* @param {Color} [lineColor=Color(0,0,0)]
|
|
1808
|
+
* @param {String} [textAlign='center']
|
|
1809
|
+
* @param {String} [font=fontDefault]
|
|
1810
|
+
* @param {CanvasRenderingContext2D} [context=overlayContext]
|
|
1811
|
+
* @memberof Draw */
|
|
1812
|
+
function drawText(text, pos, size=1, color, lineWidth=0, lineColor, textAlign, font, context)
|
|
1813
|
+
{
|
|
1814
|
+
drawTextScreen(text, worldToScreen(pos), size*cameraScale, color, lineWidth*cameraScale, lineColor, textAlign, font, context);
|
|
1815
|
+
}
|
|
1816
|
+
|
|
1793
1817
|
/** Draw text on overlay canvas in screen space
|
|
1794
1818
|
* Automatically splits new lines into rows
|
|
1795
1819
|
* @param {String} text
|
|
@@ -1799,6 +1823,8 @@ function setBlendMode(additive, useWebGL=glEnable)
|
|
|
1799
1823
|
* @param {Number} [lineWidth=0]
|
|
1800
1824
|
* @param {Color} [lineColor=Color(0,0,0)]
|
|
1801
1825
|
* @param {String} [textAlign='center']
|
|
1826
|
+
* @param {String} [font=fontDefault]
|
|
1827
|
+
* @param {CanvasRenderingContext2D} [context=overlayContext]
|
|
1802
1828
|
* @memberof Draw */
|
|
1803
1829
|
function drawTextScreen(text, pos, size=1, color=new Color, lineWidth=0, lineColor=new Color(0,0,0), textAlign='center', font=fontDefault, context=overlayContext)
|
|
1804
1830
|
{
|
|
@@ -1819,30 +1845,15 @@ function drawTextScreen(text, pos, size=1, color=new Color, lineWidth=0, lineCol
|
|
|
1819
1845
|
});
|
|
1820
1846
|
}
|
|
1821
1847
|
|
|
1822
|
-
/** Draw text on overlay canvas in world space
|
|
1823
|
-
* Automatically splits new lines into rows
|
|
1824
|
-
* @param {String} text
|
|
1825
|
-
* @param {Vector2} pos
|
|
1826
|
-
* @param {Number} [size=1]
|
|
1827
|
-
* @param {Color} [color=Color()]
|
|
1828
|
-
* @param {Number} [lineWidth=0]
|
|
1829
|
-
* @param {Color} [lineColor=Color(0,0,0)]
|
|
1830
|
-
* @param {String} [textAlign='center']
|
|
1831
|
-
* @memberof Draw */
|
|
1832
|
-
function drawText(text, pos, size=1, color, lineWidth=0, lineColor, textAlign, font)
|
|
1833
|
-
{
|
|
1834
|
-
drawTextScreen(text, worldToScreen(pos), size*cameraScale, color, lineWidth*cameraScale, lineColor, textAlign, font, mainContext);
|
|
1835
|
-
}
|
|
1836
|
-
|
|
1837
1848
|
///////////////////////////////////////////////////////////////////////////////
|
|
1838
1849
|
|
|
1839
1850
|
let engineFontImage;
|
|
1840
1851
|
|
|
1841
1852
|
/**
|
|
1842
1853
|
* Font Image Object - Draw text on a 2D canvas by using characters in an image
|
|
1843
|
-
*
|
|
1844
|
-
*
|
|
1845
|
-
*
|
|
1854
|
+
* - 96 characters (from space to tilde) are stored in an image
|
|
1855
|
+
* - Uses a default 8x8 font if none is supplied
|
|
1856
|
+
* - You can also use fonts from the main tile sheet
|
|
1846
1857
|
* @example
|
|
1847
1858
|
* // use built in font
|
|
1848
1859
|
* const font = new ImageFont;
|
|
@@ -1882,7 +1893,7 @@ class FontImage
|
|
|
1882
1893
|
{
|
|
1883
1894
|
const context = this.context;
|
|
1884
1895
|
context.save();
|
|
1885
|
-
context.imageSmoothingEnabled = !
|
|
1896
|
+
context.imageSmoothingEnabled = !canvasPixelated;
|
|
1886
1897
|
|
|
1887
1898
|
const size = this.tileSize;
|
|
1888
1899
|
const drawSize = size.add(this.paddingSize).scale(scale);
|
|
@@ -1928,7 +1939,7 @@ class FontImage
|
|
|
1928
1939
|
/** Returns true if fullscreen mode is active
|
|
1929
1940
|
* @return {Boolean}
|
|
1930
1941
|
* @memberof Draw */
|
|
1931
|
-
|
|
1942
|
+
function isFullscreen() { return document.fullscreenElement; }
|
|
1932
1943
|
|
|
1933
1944
|
/** Toggle fullsceen mode
|
|
1934
1945
|
* @memberof Draw */
|
|
@@ -1945,10 +1956,10 @@ function toggleFullscreen()
|
|
|
1945
1956
|
|
|
1946
1957
|
/**
|
|
1947
1958
|
* LittleJS Input System
|
|
1948
|
-
*
|
|
1949
|
-
*
|
|
1950
|
-
*
|
|
1951
|
-
*
|
|
1959
|
+
* - Tracks key down, pressed, and released
|
|
1960
|
+
* - Also tracks mouse buttons, position, and wheel
|
|
1961
|
+
* - Supports multiple gamepads
|
|
1962
|
+
* - Virtual gamepad for touch devices with touchGamepadSize
|
|
1952
1963
|
* @namespace Input
|
|
1953
1964
|
*/
|
|
1954
1965
|
|
|
@@ -1959,25 +1970,28 @@ function toggleFullscreen()
|
|
|
1959
1970
|
* @param {Number} [device=0]
|
|
1960
1971
|
* @return {Boolean}
|
|
1961
1972
|
* @memberof Input */
|
|
1962
|
-
|
|
1973
|
+
function keyIsDown(key, device=0)
|
|
1974
|
+
{ return inputData[device] && inputData[device][key] & 1; }
|
|
1963
1975
|
|
|
1964
1976
|
/** Returns true if device key was pressed this frame
|
|
1965
1977
|
* @param {Number} key
|
|
1966
1978
|
* @param {Number} [device=0]
|
|
1967
1979
|
* @return {Boolean}
|
|
1968
1980
|
* @memberof Input */
|
|
1969
|
-
|
|
1981
|
+
function keyWasPressed(key, device=0)
|
|
1982
|
+
{ return inputData[device] && inputData[device][key] & 2 ? 1 : 0; }
|
|
1970
1983
|
|
|
1971
1984
|
/** Returns true if device key was released this frame
|
|
1972
1985
|
* @param {Number} key
|
|
1973
1986
|
* @param {Number} [device=0]
|
|
1974
1987
|
* @return {Boolean}
|
|
1975
1988
|
* @memberof Input */
|
|
1976
|
-
|
|
1989
|
+
function keyWasReleased(key, device=0)
|
|
1990
|
+
{ return inputData[device] && inputData[device][key] & 4 ? 1 : 0; }
|
|
1977
1991
|
|
|
1978
1992
|
/** Clears all input
|
|
1979
1993
|
* @memberof Input */
|
|
1980
|
-
|
|
1994
|
+
function clearInput() { inputData = [[]]; }
|
|
1981
1995
|
|
|
1982
1996
|
/** Returns true if mouse button is down
|
|
1983
1997
|
* @function
|
|
@@ -2030,28 +2044,32 @@ let preventDefaultInput = 0;
|
|
|
2030
2044
|
* @param {Number} [gamepad=0]
|
|
2031
2045
|
* @return {Boolean}
|
|
2032
2046
|
* @memberof Input */
|
|
2033
|
-
|
|
2047
|
+
function gamepadIsDown(button, gamepad=0)
|
|
2048
|
+
{ return keyIsDown(button, gamepad+1); }
|
|
2034
2049
|
|
|
2035
2050
|
/** Returns true if gamepad button was pressed
|
|
2036
2051
|
* @param {Number} button
|
|
2037
2052
|
* @param {Number} [gamepad=0]
|
|
2038
2053
|
* @return {Boolean}
|
|
2039
2054
|
* @memberof Input */
|
|
2040
|
-
|
|
2055
|
+
function gamepadWasPressed(button, gamepad=0)
|
|
2056
|
+
{ return keyWasPressed(button, gamepad+1); }
|
|
2041
2057
|
|
|
2042
2058
|
/** Returns true if gamepad button was released
|
|
2043
2059
|
* @param {Number} button
|
|
2044
2060
|
* @param {Number} [gamepad=0]
|
|
2045
2061
|
* @return {Boolean}
|
|
2046
2062
|
* @memberof Input */
|
|
2047
|
-
|
|
2063
|
+
function gamepadWasReleased(button, gamepad=0)
|
|
2064
|
+
{ return keyWasReleased(button, gamepad+1); }
|
|
2048
2065
|
|
|
2049
2066
|
/** Returns gamepad stick value
|
|
2050
2067
|
* @param {Number} stick
|
|
2051
2068
|
* @param {Number} [gamepad=0]
|
|
2052
2069
|
* @return {Vector2}
|
|
2053
2070
|
* @memberof Input */
|
|
2054
|
-
|
|
2071
|
+
function gamepadStick(stick, gamepad=0)
|
|
2072
|
+
{ return stickData[gamepad] ? stickData[gamepad][stick] || vec2() : vec2(); }
|
|
2055
2073
|
|
|
2056
2074
|
///////////////////////////////////////////////////////////////////////////////
|
|
2057
2075
|
// Input update called by engine
|
|
@@ -2090,12 +2108,18 @@ onkeydown = (e)=>
|
|
|
2090
2108
|
e.repeat || (inputData[isUsingGamepad = 0][remapKey(e.which)] = 3);
|
|
2091
2109
|
preventDefaultInput && e.preventDefault();
|
|
2092
2110
|
}
|
|
2111
|
+
|
|
2093
2112
|
onkeyup = (e)=>
|
|
2094
2113
|
{
|
|
2095
2114
|
if (debug && e.target != document.body) return;
|
|
2096
2115
|
inputData[0][remapKey(e.which)] = 4;
|
|
2097
2116
|
}
|
|
2098
|
-
|
|
2117
|
+
|
|
2118
|
+
function remapKey(c)
|
|
2119
|
+
{
|
|
2120
|
+
return inputWASDEmulateDirection ?
|
|
2121
|
+
c==87?38 : c==83?40 : c==65?37 : c==68?39 : c : c;
|
|
2122
|
+
}
|
|
2099
2123
|
|
|
2100
2124
|
///////////////////////////////////////////////////////////////////////////////
|
|
2101
2125
|
// Mouse event handlers
|
|
@@ -2104,10 +2128,10 @@ onmousedown = (e)=> {inputData[isUsingGamepad = 0][e.button] = 3; onmousemove(e)
|
|
|
2104
2128
|
onmouseup = (e)=> inputData[0][e.button] = inputData[0][e.button] & 2 | 4;
|
|
2105
2129
|
onmousemove = (e)=> mousePosScreen = mouseToScreen(e);
|
|
2106
2130
|
onwheel = (e)=> e.ctrlKey || (mouseWheel = sign(e.deltaY));
|
|
2107
|
-
oncontextmenu = (e)=>
|
|
2131
|
+
oncontextmenu = (e)=> false; // prevent right click menu
|
|
2108
2132
|
|
|
2109
2133
|
// convert a mouse or touch event position to screen space
|
|
2110
|
-
|
|
2134
|
+
function mouseToScreen(mousePos)
|
|
2111
2135
|
{
|
|
2112
2136
|
if (!mainCanvas)
|
|
2113
2137
|
return vec2(); // fix bug that can occur if user clicks before page loads
|
|
@@ -2153,8 +2177,7 @@ function gamepadsUpdate()
|
|
|
2153
2177
|
if (gamepad)
|
|
2154
2178
|
{
|
|
2155
2179
|
// read clamp dead zone of analog sticks
|
|
2156
|
-
const deadZone = .3, deadZoneMax = .8
|
|
2157
|
-
const applyDeadZone = (v)=>
|
|
2180
|
+
const deadZone = .3, deadZoneMax = .8, applyDeadZone = (v)=>
|
|
2158
2181
|
v > deadZone ? percent( v, deadZone, deadZoneMax) :
|
|
2159
2182
|
v < -deadZone ? -percent(-v, deadZone, deadZoneMax) : 0;
|
|
2160
2183
|
|
|
@@ -2187,11 +2210,12 @@ function gamepadsUpdate()
|
|
|
2187
2210
|
/** Pulse the vibration hardware if it exists
|
|
2188
2211
|
* @param {Number} [pattern=100] - a single value in miliseconds or vibration interval array
|
|
2189
2212
|
* @memberof Input */
|
|
2190
|
-
|
|
2213
|
+
function vibrate(pattern)
|
|
2214
|
+
{ vibrateEnable && navigator && navigator.vibrate && navigator.vibrate(pattern); }
|
|
2191
2215
|
|
|
2192
2216
|
/** Cancel any ongoing vibration
|
|
2193
2217
|
* @memberof Input */
|
|
2194
|
-
|
|
2218
|
+
function vibrateStop() { vibrate(0); }
|
|
2195
2219
|
|
|
2196
2220
|
///////////////////////////////////////////////////////////////////////////////
|
|
2197
2221
|
// Touch input
|
|
@@ -2237,6 +2261,9 @@ if (isTouchDevice)
|
|
|
2237
2261
|
return true;
|
|
2238
2262
|
}
|
|
2239
2263
|
|
|
2264
|
+
// try to create touch game pad
|
|
2265
|
+
touchGamepadEnable && touchGamepadCreate();
|
|
2266
|
+
|
|
2240
2267
|
return ontouchstart(e);
|
|
2241
2268
|
}
|
|
2242
2269
|
}
|
|
@@ -2250,76 +2277,69 @@ let touchGamepadTimer = new Timer, touchGamepadButtons, touchGamepadStick;
|
|
|
2250
2277
|
// create the touch gamepad, called automatically by the engine
|
|
2251
2278
|
function touchGamepadCreate()
|
|
2252
2279
|
{
|
|
2253
|
-
if (!touchGamepadEnable || !isTouchDevice)
|
|
2254
|
-
return;
|
|
2255
|
-
|
|
2256
2280
|
// touch input internal variables
|
|
2257
2281
|
touchGamepadButtons = [];
|
|
2258
2282
|
touchGamepadStick = vec2();
|
|
2259
2283
|
|
|
2260
|
-
|
|
2261
|
-
ontouchstart = (e)=>
|
|
2284
|
+
let touchHandler = ontouchstart;
|
|
2285
|
+
ontouchstart = ontouchmove = ontouchend = (e)=>
|
|
2262
2286
|
{
|
|
2263
|
-
//
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2287
|
+
// clear touch gamepad input
|
|
2288
|
+
touchGamepadStick = vec2();
|
|
2289
|
+
touchGamepadButtons = [];
|
|
2290
|
+
|
|
2291
|
+
const touching = e.touches.length;
|
|
2292
|
+
if (touching)
|
|
2267
2293
|
{
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
touchGamepadButtons = [];
|
|
2271
|
-
|
|
2272
|
-
const touching = e.touches.length;
|
|
2273
|
-
if (touching)
|
|
2294
|
+
touchGamepadTimer.set();
|
|
2295
|
+
if (paused)
|
|
2274
2296
|
{
|
|
2275
|
-
//
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
if (paused)
|
|
2280
|
-
{
|
|
2281
|
-
// touch anywhere to press start when paused
|
|
2282
|
-
touchGamepadButtons[9] = 1;
|
|
2283
|
-
return;
|
|
2284
|
-
}
|
|
2297
|
+
// touch anywhere to press start when paused
|
|
2298
|
+
touchGamepadButtons[9] = 1;
|
|
2299
|
+
return;
|
|
2285
2300
|
}
|
|
2301
|
+
}
|
|
2286
2302
|
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2303
|
+
// get center of left and right sides
|
|
2304
|
+
const stickCenter = vec2(touchGamepadSize, mainCanvasSize.y-touchGamepadSize);
|
|
2305
|
+
const buttonCenter = mainCanvasSize.subtract(vec2(touchGamepadSize, touchGamepadSize));
|
|
2306
|
+
const startCenter = mainCanvasSize.scale(.5);
|
|
2291
2307
|
|
|
2292
|
-
|
|
2293
|
-
|
|
2308
|
+
// check each touch point
|
|
2309
|
+
for (const touch of e.touches)
|
|
2310
|
+
{
|
|
2311
|
+
const touchPos = mouseToScreen(vec2(touch.clientX, touch.clientY));
|
|
2312
|
+
if (touchPos.distance(stickCenter) < touchGamepadSize)
|
|
2294
2313
|
{
|
|
2295
|
-
|
|
2296
|
-
if (
|
|
2314
|
+
// virtual analog stick
|
|
2315
|
+
if (touchGamepadAnalog)
|
|
2316
|
+
touchGamepadStick = touchPos.subtract(stickCenter).scale(2/touchGamepadSize).clampLength();
|
|
2317
|
+
else
|
|
2297
2318
|
{
|
|
2298
|
-
//
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
else
|
|
2302
|
-
{
|
|
2303
|
-
// 8 way dpad
|
|
2304
|
-
const angle = touchPos.subtract(stickCenter).angle();
|
|
2305
|
-
touchGamepadStick.setAngle((angle * 4 / PI + 8.5 | 0) * PI / 4);
|
|
2306
|
-
}
|
|
2307
|
-
}
|
|
2308
|
-
else if (touchPos.distance(buttonCenter) < touchGamepadSize)
|
|
2309
|
-
{
|
|
2310
|
-
// virtual face buttons
|
|
2311
|
-
const button = touchPos.subtract(buttonCenter).direction();
|
|
2312
|
-
touchGamepadButtons[button] = 1;
|
|
2313
|
-
}
|
|
2314
|
-
else if (touchPos.distance(startCenter) < touchGamepadSize)
|
|
2315
|
-
{
|
|
2316
|
-
// virtual start button in center
|
|
2317
|
-
touchGamepadButtons[9] = 1;
|
|
2319
|
+
// 8 way dpad
|
|
2320
|
+
const angle = touchPos.subtract(stickCenter).angle();
|
|
2321
|
+
touchGamepadStick.setAngle((angle * 4 / PI + 8.5 | 0) * PI / 4);
|
|
2318
2322
|
}
|
|
2319
2323
|
}
|
|
2324
|
+
else if (touchPos.distance(buttonCenter) < touchGamepadSize)
|
|
2325
|
+
{
|
|
2326
|
+
// virtual face buttons
|
|
2327
|
+
const button = touchPos.subtract(buttonCenter).direction();
|
|
2328
|
+
touchGamepadButtons[button] = 1;
|
|
2329
|
+
}
|
|
2330
|
+
else if (touchPos.distance(startCenter) < touchGamepadSize)
|
|
2331
|
+
{
|
|
2332
|
+
// virtual start button in center
|
|
2333
|
+
touchGamepadButtons[9] = 1;
|
|
2334
|
+
}
|
|
2320
2335
|
}
|
|
2321
2336
|
|
|
2322
|
-
|
|
2337
|
+
// call default touch handler and set to using gamepad
|
|
2338
|
+
touchHandler(e);
|
|
2339
|
+
isUsingGamepad = 1;
|
|
2340
|
+
|
|
2341
|
+
// must return true so the document will get focus
|
|
2342
|
+
return true;
|
|
2323
2343
|
}
|
|
2324
2344
|
}
|
|
2325
2345
|
|
|
@@ -2345,7 +2365,7 @@ function touchGamepadRender()
|
|
|
2345
2365
|
overlayContext.beginPath();
|
|
2346
2366
|
|
|
2347
2367
|
const leftCenter = vec2(touchGamepadSize, mainCanvasSize.y-touchGamepadSize);
|
|
2348
|
-
if (touchGamepadAnalog)
|
|
2368
|
+
if (touchGamepadAnalog) // draw circle shaped gamepad
|
|
2349
2369
|
{
|
|
2350
2370
|
overlayContext.arc(leftCenter.x, leftCenter.y, touchGamepadSize/2, 0, 9);
|
|
2351
2371
|
overlayContext.fill();
|
|
@@ -2380,20 +2400,21 @@ function touchGamepadRender()
|
|
|
2380
2400
|
}
|
|
2381
2401
|
/**
|
|
2382
2402
|
* LittleJS Audio System
|
|
2383
|
-
*
|
|
2384
|
-
*
|
|
2385
|
-
*
|
|
2386
|
-
*
|
|
2387
|
-
*
|
|
2388
|
-
*
|
|
2403
|
+
* - <a href=https://killedbyapixel.github.io/ZzFX/>ZzFX Sound Effects</a> - Sound Effect Generator
|
|
2404
|
+
* - <a href=https://keithclark.github.io/ZzFXM/>ZzFXM Music</a> - Music System
|
|
2405
|
+
* - Caches sounds and music for fast playback
|
|
2406
|
+
* - Can attenuate and apply stereo panning to sounds
|
|
2407
|
+
* - Ability to play mp3, ogg, and wave files
|
|
2408
|
+
* - Speech synthesis wrapper functions
|
|
2409
|
+
* @namespace Audio
|
|
2389
2410
|
*/
|
|
2390
2411
|
|
|
2391
2412
|
'use strict';
|
|
2392
2413
|
|
|
2393
2414
|
/**
|
|
2394
2415
|
* Sound Object - Stores a zzfx sound for later use and can be played positionally
|
|
2395
|
-
*
|
|
2396
|
-
* <
|
|
2416
|
+
*
|
|
2417
|
+
* <a href=https://killedbyapixel.github.io/ZzFX/>Create sounds using the ZzFX Sound Designer.</a>
|
|
2397
2418
|
* @example
|
|
2398
2419
|
* // create a sound
|
|
2399
2420
|
* const sound_example = new Sound([.5,.5]);
|
|
@@ -2477,8 +2498,8 @@ class Sound
|
|
|
2477
2498
|
|
|
2478
2499
|
/**
|
|
2479
2500
|
* Music Object - Stores a zzfx music track for later use
|
|
2480
|
-
*
|
|
2481
|
-
* <
|
|
2501
|
+
*
|
|
2502
|
+
* <a href=https://keithclark.github.io/ZzFXM/>Create music with the ZzFXM tracker.</a>
|
|
2482
2503
|
* @example
|
|
2483
2504
|
* // create some music
|
|
2484
2505
|
* const music_example = new Music(
|
|
@@ -2588,14 +2609,15 @@ function speak(text, language='', volume=1, rate=1, pitch=1)
|
|
|
2588
2609
|
|
|
2589
2610
|
/** Stop all queued speech
|
|
2590
2611
|
* @memberof Audio */
|
|
2591
|
-
|
|
2612
|
+
function speakStop() {speechSynthesis && speechSynthesis.cancel();}
|
|
2592
2613
|
|
|
2593
2614
|
/** Get frequency of a note on a musical scale
|
|
2594
2615
|
* @param {Number} semitoneOffset - How many semitones away from the root note
|
|
2595
2616
|
* @param {Number} [rootNoteFrequency=220] - Frequency at semitone offset 0
|
|
2596
2617
|
* @return {Number} - The frequency of the note
|
|
2597
2618
|
* @memberof Audio */
|
|
2598
|
-
|
|
2619
|
+
function getNoteFrequency(semitoneOffset, rootFrequency=220)
|
|
2620
|
+
{ return rootFrequency * 2**(semitoneOffset/12); }
|
|
2599
2621
|
|
|
2600
2622
|
///////////////////////////////////////////////////////////////////////////////
|
|
2601
2623
|
|
|
@@ -2650,15 +2672,15 @@ function playSamples(sampleChannels, volume=1, rate=1, pan=0, loop=0)
|
|
|
2650
2672
|
}
|
|
2651
2673
|
|
|
2652
2674
|
///////////////////////////////////////////////////////////////////////////////
|
|
2653
|
-
// ZzFXMicro - Zuper Zmall Zound Zynth - v1.
|
|
2675
|
+
// ZzFXMicro - Zuper Zmall Zound Zynth - v1.2.0 by Frank Force
|
|
2654
2676
|
|
|
2655
2677
|
/** Generate and play a ZzFX sound
|
|
2656
|
-
*
|
|
2657
|
-
* <
|
|
2678
|
+
*
|
|
2679
|
+
* <a href=https://killedbyapixel.github.io/ZzFX/>Create sounds using the ZzFX Sound Designer.</a>
|
|
2658
2680
|
* @param {Array} zzfxSound - Array of ZzFX parameters, ex. [.5,.5]
|
|
2659
|
-
* @return {
|
|
2681
|
+
* @return {AudioBufferSourceNode} - The audio node of the sound played
|
|
2660
2682
|
* @memberof Audio */
|
|
2661
|
-
|
|
2683
|
+
function zzfx(...zzfxSound) { return playSamples([zzfxG(...zzfxSound)]); }
|
|
2662
2684
|
|
|
2663
2685
|
/** Sample rate used for all ZzFX sounds
|
|
2664
2686
|
* @default 44100
|
|
@@ -2666,7 +2688,29 @@ const zzfx = (...zzfxSound) => playSamples([zzfxG(...zzfxSound)]);
|
|
|
2666
2688
|
const zzfxR = 44100;
|
|
2667
2689
|
|
|
2668
2690
|
/** Generate samples for a ZzFX sound
|
|
2669
|
-
* @
|
|
2691
|
+
* @param {Number} [volume=1] - Volume scale (percent)
|
|
2692
|
+
* @param {Number} [randomness=.05] - How much to randomize frequency (percent Hz)
|
|
2693
|
+
* @param {Number} [frequency=220] - Frequency of sound (Hz)
|
|
2694
|
+
* @param {Number} [attack=0] - Attack time, how fast sound starts (seconds)
|
|
2695
|
+
* @param {Number} [sustain=0] - Sustain time, how long sound holds (seconds)
|
|
2696
|
+
* @param {Number} [release=.1] - Release time, how fast sound fades out (seconds)
|
|
2697
|
+
* @param {Number} [shape=0] - Shape of the sound wave
|
|
2698
|
+
* @param {Number} [shapeCurve=1] - Squarenes of wave (0=square, 1=normal, 2=pointy)
|
|
2699
|
+
* @param {Number} [slide=0] - How much to slide frequency (kHz/s)
|
|
2700
|
+
* @param {Number} [deltaSlide=0] - How much to change slide (kHz/s/s)
|
|
2701
|
+
* @param {Number} [pitchJump=0] - Frequency of pitch jump (Hz)
|
|
2702
|
+
* @param {Number} [pitchJumpTime=0] - Time of pitch jump (seconds)
|
|
2703
|
+
* @param {Number} [repeatTime=0] - Resets some parameters periodically (seconds)
|
|
2704
|
+
* @param {Number} [noise=0] - How much random noise to add (percent)
|
|
2705
|
+
* @param {Number} [modulation=0] - Frequency of modulation wave, negative flips phase (Hz)
|
|
2706
|
+
* @param {Number} [bitCrush=0] - Resamples at a lower frequency in (samples*100)
|
|
2707
|
+
* @param {Number} [delay=0] - Overlap sound with itself for reverb and flanger effects (seconds)
|
|
2708
|
+
* @param {Number} [sustainVolume=1] - Volume level for sustain (percent)
|
|
2709
|
+
* @param {Number} [decay=0] - Decay time, how long to reach sustain after attack (seconds)
|
|
2710
|
+
* @param {Number} [tremolo=0] - Trembling effect, rate controlled by repeat time (precent)
|
|
2711
|
+
* @return {Array} - Array of audio samples
|
|
2712
|
+
* @memberof Audio
|
|
2713
|
+
*/
|
|
2670
2714
|
function zzfxG
|
|
2671
2715
|
(
|
|
2672
2716
|
// parameters
|
|
@@ -2676,11 +2720,11 @@ function zzfxG
|
|
|
2676
2720
|
bitCrush = 0, delay = 0, sustainVolume = 1, decay = 0, tremolo = 0
|
|
2677
2721
|
)
|
|
2678
2722
|
{
|
|
2679
|
-
//
|
|
2723
|
+
// locals
|
|
2680
2724
|
let PI2 = PI*2, startSlide = slide *= 500 * PI2 / zzfxR / zzfxR, b=[],
|
|
2681
2725
|
startFrequency = frequency *= (1 + randomness*rand(-1,1)) * PI2 / zzfxR,
|
|
2682
|
-
t=0, tm=0, i=0, j=1, r=0, c=0, s=0, f, length
|
|
2683
|
-
|
|
2726
|
+
t=0, tm=0, i=0, j=1, r=0, c=0, s=0, f, length
|
|
2727
|
+
|
|
2684
2728
|
// scale by sample rate
|
|
2685
2729
|
attack = attack * zzfxR + 9; // minimum attack to prevent pop
|
|
2686
2730
|
decay *= zzfxR;
|
|
@@ -2730,18 +2774,18 @@ function zzfxG
|
|
|
2730
2774
|
Math.cos(modulation*tm++); // modulation
|
|
2731
2775
|
t += f - f*noise*(1 - (Math.sin(i)+1)*1e9%2); // noise
|
|
2732
2776
|
|
|
2733
|
-
if (j && ++j > pitchJumpTime)
|
|
2777
|
+
if (j && ++j > pitchJumpTime) // pitch jump
|
|
2734
2778
|
{
|
|
2735
|
-
frequency += pitchJump;
|
|
2736
|
-
startFrequency += pitchJump;
|
|
2737
|
-
j = 0;
|
|
2779
|
+
frequency += pitchJump; // apply pitch jump
|
|
2780
|
+
startFrequency += pitchJump; // also apply to start
|
|
2781
|
+
j = 0; // reset pitch jump time
|
|
2738
2782
|
}
|
|
2739
2783
|
|
|
2740
2784
|
if (repeatTime && !(++r % repeatTime)) // repeat
|
|
2741
2785
|
{
|
|
2742
|
-
frequency = startFrequency;
|
|
2743
|
-
slide = startSlide;
|
|
2744
|
-
j
|
|
2786
|
+
frequency = startFrequency; // reset frequency
|
|
2787
|
+
slide = startSlide; // reset slide
|
|
2788
|
+
j ||= 1; // reset pitch jump time
|
|
2745
2789
|
}
|
|
2746
2790
|
}
|
|
2747
2791
|
|
|
@@ -2756,7 +2800,7 @@ function zzfxG
|
|
|
2756
2800
|
* @param {Array} patterns - Array of pattern data
|
|
2757
2801
|
* @param {Array} sequence - Array of pattern indexes
|
|
2758
2802
|
* @param {Number} [BPM=125] - Playback speed of the song in BPM
|
|
2759
|
-
* @
|
|
2803
|
+
* @return {Array} - Left and right channel sample data
|
|
2760
2804
|
* @memberof Audio */
|
|
2761
2805
|
function zzfxM(instruments, patterns, sequence, BPM = 125)
|
|
2762
2806
|
{
|
|
@@ -2854,13 +2898,13 @@ function zzfxM(instruments, patterns, sequence, BPM = 125)
|
|
|
2854
2898
|
}
|
|
2855
2899
|
/**
|
|
2856
2900
|
* LittleJS Tile Layer System
|
|
2857
|
-
*
|
|
2858
|
-
*
|
|
2859
|
-
*
|
|
2860
|
-
*
|
|
2861
|
-
*
|
|
2862
|
-
*
|
|
2863
|
-
*
|
|
2901
|
+
* - Caches arrays of tiles to off screen canvas for fast rendering
|
|
2902
|
+
* - Unlimted numbers of layers, allocates canvases as needed
|
|
2903
|
+
* - Interfaces with EngineObject for collision
|
|
2904
|
+
* - Collision layer is separate from visible layers
|
|
2905
|
+
* - It is recommended to have a visible layer that matches the collision
|
|
2906
|
+
* - Tile layers can be drawn to using their context with canvas2d
|
|
2907
|
+
* - Drawn directly to the main canvas without using WebGL
|
|
2864
2908
|
* @namespace TileCollision
|
|
2865
2909
|
*/
|
|
2866
2910
|
|
|
@@ -2891,15 +2935,19 @@ function initTileCollision(size)
|
|
|
2891
2935
|
* @param {Vector2} pos
|
|
2892
2936
|
* @param {Number} [data=0]
|
|
2893
2937
|
* @memberof TileCollision */
|
|
2894
|
-
|
|
2938
|
+
function setTileCollisionData(pos, data=0)
|
|
2939
|
+
{
|
|
2895
2940
|
pos.arrayCheck(tileCollisionSize) && (tileCollision[(pos.y|0)*tileCollisionSize.x+pos.x|0] = data);
|
|
2941
|
+
}
|
|
2896
2942
|
|
|
2897
2943
|
/** Get tile collision data
|
|
2898
2944
|
* @param {Vector2} pos
|
|
2899
2945
|
* @return {Number}
|
|
2900
2946
|
* @memberof TileCollision */
|
|
2901
|
-
|
|
2902
|
-
|
|
2947
|
+
function getTileCollisionData(pos)
|
|
2948
|
+
{
|
|
2949
|
+
return pos.arrayCheck(tileCollisionSize) ? tileCollision[(pos.y|0)*tileCollisionSize.x+pos.x|0] : 0;
|
|
2950
|
+
}
|
|
2903
2951
|
|
|
2904
2952
|
/** Check if collision with another object should occur
|
|
2905
2953
|
* @param {Vector2} pos
|
|
@@ -2993,10 +3041,10 @@ class TileLayerData
|
|
|
2993
3041
|
|
|
2994
3042
|
/**
|
|
2995
3043
|
* Tile layer object - cached rendering system for tile layers
|
|
2996
|
-
*
|
|
2997
|
-
*
|
|
2998
|
-
*
|
|
2999
|
-
*
|
|
3044
|
+
* - Each Tile layer is rendered to an off screen canvas
|
|
3045
|
+
* - To allow dynamic modifications, layers are rendered using canvas 2d
|
|
3046
|
+
* - Some devices like mobile phones are limited to 4k texture resolution
|
|
3047
|
+
* - So with 16x16 tiles this limits layers to 256x256 on mobile devices
|
|
3000
3048
|
* @extends EngineObject
|
|
3001
3049
|
* @example
|
|
3002
3050
|
* // create tile collision and visible tile layer
|
|
@@ -3196,12 +3244,9 @@ constructor(pos, size=tileCollisionSize, tileSize=tileSizeDefault, scale=vec2(1)
|
|
|
3196
3244
|
drawRect(pos, size, color, angle)
|
|
3197
3245
|
{ this.drawTile(pos, size, -1, 0, color, angle); }
|
|
3198
3246
|
}
|
|
3199
|
-
|
|
3200
|
-
|
|
3201
|
-
|
|
3202
|
-
- Updates particle physics
|
|
3203
|
-
- Fast particle rendering
|
|
3204
|
-
*/
|
|
3247
|
+
/**
|
|
3248
|
+
* LittleJS Particle System
|
|
3249
|
+
*/
|
|
3205
3250
|
|
|
3206
3251
|
'use strict';
|
|
3207
3252
|
|
|
@@ -3507,9 +3552,9 @@ class Particle extends EngineObject
|
|
|
3507
3552
|
}
|
|
3508
3553
|
/**
|
|
3509
3554
|
* LittleJS Medal System
|
|
3510
|
-
*
|
|
3511
|
-
*
|
|
3512
|
-
*
|
|
3555
|
+
* - Tracks and displays medals
|
|
3556
|
+
* - Saves medals to local storage
|
|
3557
|
+
* - Newgrounds integration
|
|
3513
3558
|
* @namespace Medals
|
|
3514
3559
|
*/
|
|
3515
3560
|
|
|
@@ -3526,8 +3571,8 @@ let medalsDisplayQueue = [], medalsSaveName, medalsDisplayTimeLast;
|
|
|
3526
3571
|
///////////////////////////////////////////////////////////////////////////////
|
|
3527
3572
|
|
|
3528
3573
|
/** Initialize medals with a save name used for storage
|
|
3529
|
-
*
|
|
3530
|
-
*
|
|
3574
|
+
* - Call this after creating all medals
|
|
3575
|
+
* - Checks if medals are unlocked
|
|
3531
3576
|
* @param {String} saveName
|
|
3532
3577
|
* @memberof Medals */
|
|
3533
3578
|
function medalsInit(saveName)
|
|
@@ -3797,20 +3842,43 @@ class Newgrounds
|
|
|
3797
3842
|
CryptoJS()
|
|
3798
3843
|
{
|
|
3799
3844
|
///////////////////////////////////////////////////////////////////////////////
|
|
3800
|
-
// Crypto-JS - https://github.com/brix/crypto-js
|
|
3801
|
-
//
|
|
3802
|
-
|
|
3845
|
+
// Crypto-JS - https://github.com/brix/crypto-js - MIT License
|
|
3846
|
+
//
|
|
3847
|
+
// [The MIT License (MIT)](http://opensource.org/licenses/MIT)
|
|
3848
|
+
//
|
|
3849
|
+
// Copyright (c) 2009-2013 Jeff Mott
|
|
3850
|
+
// Copyright (c) 2013-2016 Evan Vosberg
|
|
3851
|
+
//
|
|
3852
|
+
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
3853
|
+
// of this software and associated documentation files (the "Software"), to deal
|
|
3854
|
+
// in the Software without restriction, including without limitation the rights
|
|
3855
|
+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
3856
|
+
// copies of the Software, and to permit persons to whom the Software is
|
|
3857
|
+
// furnished to do so, subject to the following conditions:
|
|
3858
|
+
//
|
|
3859
|
+
// The above copyright notice and this permission notice shall be included in
|
|
3860
|
+
// all copies or substantial portions of the Software.
|
|
3861
|
+
//
|
|
3862
|
+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
3863
|
+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
3864
|
+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
3865
|
+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
3866
|
+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
3867
|
+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
3868
|
+
// THE SOFTWARE.
|
|
3803
3869
|
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));
|
|
3870
|
+
// end of Crypto-JS
|
|
3871
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
3804
3872
|
}
|
|
3805
3873
|
}
|
|
3806
3874
|
/**
|
|
3807
3875
|
* LittleJS WebGL Interface
|
|
3808
|
-
*
|
|
3809
|
-
*
|
|
3810
|
-
*
|
|
3811
|
-
*
|
|
3812
|
-
*
|
|
3813
|
-
*
|
|
3876
|
+
* - All webgl used by the engine is wrapped up here
|
|
3877
|
+
* - For normal stuff you won't need to see or call anything in this file
|
|
3878
|
+
* - For advanced stuff there are helper functions to create shaders, textures, etc
|
|
3879
|
+
* - Can be disabled with glEnable to revert to 2D canvas rendering
|
|
3880
|
+
* - Batches sprite rendering on GPU for incredibly fast performance
|
|
3881
|
+
* - Sprite transform math is done in the shader where possible
|
|
3814
3882
|
* @namespace WebGL
|
|
3815
3883
|
*/
|
|
3816
3884
|
|
|
@@ -3885,7 +3953,7 @@ function glSetBlendMode(additive)
|
|
|
3885
3953
|
}
|
|
3886
3954
|
|
|
3887
3955
|
/** Set the WebGl texture, not normally necessary unless multiple tile sheets are used
|
|
3888
|
-
*
|
|
3956
|
+
* - This may also flush the gl buffer resulting in more draw calls and worse performance
|
|
3889
3957
|
* @param {WebGLTexture} [texture=glTileTexture]
|
|
3890
3958
|
* @memberof WebGL */
|
|
3891
3959
|
function glSetTexture(texture=glTileTexture)
|
|
@@ -3946,7 +4014,7 @@ function glCreateTexture(image)
|
|
|
3946
4014
|
image && image.width && glContext.texImage2D(gl_TEXTURE_2D, 0, gl_RGBA, gl_RGBA, gl_UNSIGNED_BYTE, image);
|
|
3947
4015
|
|
|
3948
4016
|
// use point filtering for pixelated rendering
|
|
3949
|
-
const filter =
|
|
4017
|
+
const filter = canvasPixelated ? gl_NEAREST : gl_LINEAR;
|
|
3950
4018
|
glContext.texParameteri(gl_TEXTURE_2D, gl_TEXTURE_MIN_FILTER, filter);
|
|
3951
4019
|
glContext.texParameteri(gl_TEXTURE_2D, gl_TEXTURE_MAG_FILTER, filter);
|
|
3952
4020
|
glContext.texParameteri(gl_TEXTURE_2D, gl_TEXTURE_WRAP_S, gl_CLAMP_TO_EDGE);
|