littlejsengine 1.9.7 → 1.9.8

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.
@@ -29,13 +29,16 @@ export {
29
29
  // Debug
30
30
  ASSERT,
31
31
  debugRect,
32
+ debugPoly,
32
33
  debugCircle,
33
34
  debugPoint,
34
35
  debugLine,
35
- debugAABB,
36
+ debugOverlap,
36
37
  debugText,
37
38
  debugClear,
38
39
  debugSaveCanvas,
40
+ debugSaveText,
41
+ debugSaveDataURL,
39
42
 
40
43
  // Settings
41
44
  cameraPos,
@@ -187,7 +190,6 @@ export {
187
190
  glCompileShader,
188
191
  glCreateProgram,
189
192
  glCreateTexture,
190
- glInitPostProcess,
191
193
 
192
194
  // Input
193
195
  keyIsDown,
@@ -246,7 +248,5 @@ export {
246
248
  medals,
247
249
  medalsPreventUnlock,
248
250
  medalsInit,
249
- newgroundsInit,
250
251
  Medal,
251
- Newgrounds,
252
252
  };
@@ -9,9 +9,9 @@
9
9
  'use strict';
10
10
 
11
11
  /** List of all medals
12
- * @type {Array}
12
+ * @type {Object}
13
13
  * @memberof Medals */
14
- const medals = [];
14
+ const medals = {};
15
15
 
16
16
  // Engine internal variables not exposed to documentation
17
17
  let medalsDisplayQueue = [], medalsSaveName, medalsDisplayTimeLast;
@@ -27,9 +27,45 @@ function medalsInit(saveName)
27
27
  {
28
28
  // check if medals are unlocked
29
29
  medalsSaveName = saveName;
30
- debugMedals || medals.forEach(medal=> medal.unlocked = (localStorage[medal.storageKey()] | 0));
30
+ if (!debugMedals)
31
+ medalsForEach(medal=> medal.unlocked = (localStorage[medal.storageKey()] | 0));
32
+
33
+ // engine automatically renders medals
34
+ addPluginRender(function()
35
+ {
36
+ if (!medalsDisplayQueue.length)
37
+ return;
38
+
39
+ // update first medal in queue
40
+ const medal = medalsDisplayQueue[0];
41
+ const time = timeReal - medalsDisplayTimeLast;
42
+ if (!medalsDisplayTimeLast)
43
+ medalsDisplayTimeLast = timeReal;
44
+ else if (time > medalDisplayTime)
45
+ {
46
+ medalsDisplayTimeLast = 0;
47
+ medalsDisplayQueue.shift();
48
+ }
49
+ else
50
+ {
51
+ // slide on/off medals
52
+ const slideOffTime = medalDisplayTime - medalDisplaySlideTime;
53
+ const hidePercent =
54
+ time < medalDisplaySlideTime ? 1 - time / medalDisplaySlideTime :
55
+ time > slideOffTime ? (time - slideOffTime) / medalDisplaySlideTime : 0;
56
+ medal.render(hidePercent);
57
+ }
58
+ });
31
59
  }
32
60
 
61
+ /** Calls a function for each medal
62
+ * @param {Function} callback
63
+ * @memberof Medals */
64
+ function medalsForEach(callback)
65
+ { Object.values(medals).forEach(medal=>callback(medal)); }
66
+
67
+ ///////////////////////////////////////////////////////////////////////////////
68
+
33
69
  /**
34
70
  * Medal - Tracks an unlockable medal
35
71
  * @example
@@ -74,7 +110,6 @@ class Medal
74
110
  ASSERT(medalsSaveName, 'save name must be set');
75
111
  localStorage[this.storageKey()] = this.unlocked = 1;
76
112
  medalsDisplayQueue.push(this);
77
- newgrounds && newgrounds.unlockMedal(this.id);
78
113
  }
79
114
 
80
115
  /** Render a medal
@@ -122,171 +157,4 @@ class Medal
122
157
 
123
158
  // Get local storage key used by the medal
124
159
  storageKey() { return medalsSaveName + '_' + this.id; }
125
- }
126
-
127
- // engine automatically renders medals
128
- function medalsRender()
129
- {
130
- if (!medalsDisplayQueue.length)
131
- return;
132
-
133
- // update first medal in queue
134
- const medal = medalsDisplayQueue[0];
135
- const time = timeReal - medalsDisplayTimeLast;
136
- if (!medalsDisplayTimeLast)
137
- medalsDisplayTimeLast = timeReal;
138
- else if (time > medalDisplayTime)
139
- {
140
- medalsDisplayTimeLast = 0;
141
- medalsDisplayQueue.shift();
142
- }
143
- else
144
- {
145
- // slide on/off medals
146
- const slideOffTime = medalDisplayTime - medalDisplaySlideTime;
147
- const hidePercent =
148
- time < medalDisplaySlideTime ? 1 - time / medalDisplaySlideTime :
149
- time > slideOffTime ? (time - slideOffTime) / medalDisplaySlideTime : 0;
150
- medal.render(hidePercent);
151
- }
152
- }
153
-
154
- ///////////////////////////////////////////////////////////////////////////////
155
-
156
- // global Newgrounds object
157
- let newgrounds;
158
-
159
- /** This can used to enable Newgrounds functionality
160
- * @param {Number} app_id - The newgrounds App ID
161
- * @param {String} [cipher] - The encryption Key (AES-128/Base64)
162
- * @param {Object} [cryptoJS] - An instance of CryptoJS, if there is a cipher
163
- * @memberof Medals */
164
- function newgroundsInit(app_id, cipher, cryptoJS)
165
- { newgrounds = new Newgrounds(app_id, cipher, cryptoJS); }
166
-
167
- /**
168
- * Newgrounds API wrapper object
169
- * @example
170
- * // create a newgrounds object, replace the app id with your own
171
- * const app_id = '53123:1ZuSTQ9l';
172
- * newgrounds = new Newgrounds(app_id);
173
- */
174
- class Newgrounds
175
- {
176
- /** Create a newgrounds object
177
- * @param {Number} app_id - The newgrounds App ID
178
- * @param {String} [cipher] - The encryption Key (AES-128/Base64)
179
- * @param {Object} [cryptoJS] - An instance of CryptoJS, if there is a cipher */
180
- constructor(app_id, cipher, cryptoJS)
181
- {
182
- ASSERT(!newgrounds && app_id>0, 'there can only be one newgrounds object');
183
- ASSERT(!cipher || cryptoJS, 'must provide cryptojs if there is a cipher');
184
-
185
- this.app_id = app_id;
186
- this.cipher = cipher;
187
- this.cryptoJS = cryptoJS;
188
- this.host = location ? location.hostname : '';
189
-
190
- // get session id from url search params
191
- const url = new URL(location.href);
192
- this.session_id = url.searchParams.get('ngio_session_id');
193
-
194
- if (!this.session_id)
195
- return; // only use newgrounds when logged in
196
-
197
- // get medals
198
- const medalsResult = this.call('Medal.getList');
199
- this.medals = medalsResult ? medalsResult.result.data['medals'] : [];
200
- debugMedals && console.log(this.medals);
201
- for (const newgroundsMedal of this.medals)
202
- {
203
- const medal = medals[newgroundsMedal['id']];
204
- if (medal)
205
- {
206
- // copy newgrounds medal data
207
- medal.image = new Image;
208
- medal.image.src = newgroundsMedal['icon'];
209
- medal.name = newgroundsMedal['name'];
210
- medal.description = newgroundsMedal['description'];
211
- medal.unlocked = newgroundsMedal['unlocked'];
212
- medal.difficulty = newgroundsMedal['difficulty'];
213
- medal.value = newgroundsMedal['value'];
214
-
215
- if (medal.value)
216
- medal.description = medal.description + ' (' + medal.value + ')';
217
- }
218
- }
219
-
220
- // get scoreboards
221
- const scoreboardResult = this.call('ScoreBoard.getBoards');
222
- this.scoreboards = scoreboardResult ? scoreboardResult.result.data.scoreboards : [];
223
- debugMedals && console.log(this.scoreboards);
224
-
225
- const keepAliveMS = 5 * 60 * 1e3;
226
- setInterval(()=>this.call('Gateway.ping', 0, true), keepAliveMS);
227
- }
228
-
229
- /** Send message to unlock a medal by id
230
- * @param {Number} id - The medal id */
231
- unlockMedal(id) { return this.call('Medal.unlock', {'id':id}, true); }
232
-
233
- /** Send message to post score
234
- * @param {Number} id - The scoreboard id
235
- * @param {Number} value - The score value */
236
- postScore(id, value) { return this.call('ScoreBoard.postScore', {'id':id, 'value':value}, true); }
237
-
238
- /** Get scores from a scoreboard
239
- * @param {Number} id - The scoreboard id
240
- * @param {String} [user] - A user's id or name
241
- * @param {Number} [social] - If true, only social scores will be loaded
242
- * @param {Number} [skip] - Number of scores to skip before start
243
- * @param {Number} [limit] - Number of scores to include in the list
244
- * @return {Object} - The response JSON object
245
- */
246
- getScores(id, user, social=0, skip=0, limit=10)
247
- { return this.call('ScoreBoard.getScores', {'id':id, 'user':user, 'social':social, 'skip':skip, 'limit':limit}); }
248
-
249
- /** Send message to log a view */
250
- logView() { return this.call('App.logView', {'host':this.host}, true); }
251
-
252
- /** Send a message to call a component of the Newgrounds API
253
- * @param {String} component - Name of the component
254
- * @param {Object} [parameters] - Parameters to use for call
255
- * @param {Boolean} [async] - If true, don't wait for response before continuing
256
- * @return {Object} - The response JSON object
257
- */
258
- call(component, parameters, async=false)
259
- {
260
- const call = {'component':component, 'parameters':parameters};
261
- if (this.cipher)
262
- {
263
- // encrypt using AES-128 Base64 with cryptoJS
264
- const cryptoJS = this.cryptoJS;
265
- const aesKey = cryptoJS['enc']['Base64']['parse'](this.cipher);
266
- const iv = cryptoJS['lib']['WordArray']['random'](16);
267
- const encrypted = cryptoJS['AES']['encrypt'](JSON.stringify(call), aesKey, {'iv':iv});
268
- call['secure'] = cryptoJS['enc']['Base64']['stringify'](iv.concat(encrypted['ciphertext']));
269
- call['parameters'] = 0;
270
- }
271
-
272
- // build the input object
273
- const input =
274
- {
275
- 'app_id': this.app_id,
276
- 'session_id': this.session_id,
277
- 'call': call
278
- };
279
-
280
- // build post data
281
- const formData = new FormData();
282
- formData.append('input', JSON.stringify(input));
283
-
284
- // send post data
285
- const xmlHttp = new XMLHttpRequest();
286
- const url = 'https://newgrounds.io/gateway_v3.php';
287
- xmlHttp.open('POST', url, !debugMedals && async);
288
- xmlHttp.send(formData);
289
- debugMedals && console.log(xmlHttp.responseText);
290
- return xmlHttp.responseText && JSON.parse(xmlHttp.responseText);
291
- }
292
160
  }
@@ -205,7 +205,7 @@ class EngineObject
205
205
  if (o.mass) // push away if not fixed
206
206
  o.velocity = o.velocity.subtract(velocity);
207
207
 
208
- debugOverlay && debugPhysics && debugAABB(this.pos, this.size, o.pos, o.size, '#f00');
208
+ debugOverlay && debugPhysics && debugOverlap(this.pos, this.size, o.pos, o.size, '#f00');
209
209
  continue;
210
210
  }
211
211
 
@@ -267,7 +267,7 @@ class EngineObject
267
267
  else // bounce if other object is fixed
268
268
  this.velocity.x *= -elasticity;
269
269
  }
270
- debugOverlay && debugPhysics && debugAABB(this.pos, this.size, o.pos, o.size, '#f0f');
270
+ debugOverlay && debugPhysics && debugOverlap(this.pos, this.size, o.pos, o.size, '#f0f');
271
271
  }
272
272
  }
273
273
  if (this.collideTiles)
@@ -328,6 +328,22 @@ class EngineObject
328
328
  for (const child of this.children)
329
329
  child.destroy(child.parent = 0);
330
330
  }
331
+
332
+ /** Convert from local space to world space
333
+ * @param {Vector2} pos - local space point */
334
+ localToWorld(pos) { return this.pos.add(pos.rotate(-this.angle)); }
335
+
336
+ /** Convert from world space to local space
337
+ * @param {Vector2} pos - world space point */
338
+ worldToLocal(pos) { return pos.subtract(this.pos).rotate(this.angle); }
339
+
340
+ /** Convert from local space to world space for a vector (rotation only)
341
+ * @param {Vector2} vec - local space vector */
342
+ localToWorldVector(vec) { return vec.rotate(this.angle); }
343
+
344
+ /** Convert from world space to local space for a vector (rotation only)
345
+ * @param {Vector2} vec - world space vector */
346
+ worldToLocalVector(vec) { return vec.rotate(-this.angle); }
331
347
 
332
348
  /** Called to check if a tile collision should be resolved
333
349
  * @param {Number} tileData - the value of the tile at the position
@@ -414,4 +430,16 @@ class EngineObject
414
430
  return text;
415
431
  }
416
432
  }
433
+
434
+ /** Render debug info for this object */
435
+ renderDebugInfo()
436
+ {
437
+ // show object info for debugging
438
+ const size = vec2(max(this.size.x, .2), max(this.size.y, .2));
439
+ const color1 = rgb(this.collideTiles?1:0, this.collideSolidObjects?1:0, this.isSolid?1:0, this.parent?.2:.5);
440
+ const color2 = this.parent ? rgb(1,1,1,.5) : rgb(0,0,0,.8);
441
+ drawRect(this.pos, size, color1, this.angle, false);
442
+ drawRect(this.pos, size.scale(.8), color2, this.angle, false);
443
+ this.parent && drawLine(this.pos, this.parent.pos, .1, rgb(0,0,1,.5), false);
444
+ }
417
445
  }
@@ -22,10 +22,13 @@ function debugInit (){}
22
22
  function debugUpdate (){}
23
23
  function debugRender (){}
24
24
  function debugRect (){}
25
+ function debugPoly (){}
25
26
  function debugCircle (){}
26
27
  function debugPoint (){}
27
28
  function debugLine (){}
28
- function debugAABB (){}
29
+ function debugOverlap (){}
29
30
  function debugText (){}
30
31
  function debugClear (){}
31
- function debugSaveCanvas (){}
32
+ function debugSaveCanvas (){}
33
+ function debugSaveText (){}
34
+ function debugSaveDataURL(){}
@@ -26,9 +26,9 @@ let cameraScale = 32;
26
26
 
27
27
  /** The max size of the canvas, centered if window is larger
28
28
  * @type {Vector2}
29
- * @default Vector2(1920,1200)
29
+ * @default Vector2(1920,1080)
30
30
  * @memberof Settings */
31
- let canvasMaxSize = vec2(1920, 1200);
31
+ let canvasMaxSize = vec2(1920, 1080);
32
32
 
33
33
  /** Fixed size of the canvas, if enabled canvas size never changes
34
34
  * - you may also need to set mainCanvasSize if using screen space coords in startup
@@ -301,7 +301,7 @@ class RandomGenerator
301
301
  */
302
302
  function vec2(x=0, y)
303
303
  {
304
- return typeof x === 'number' ?
304
+ return typeof x == 'number' ?
305
305
  new Vector2(x, y == undefined? x : y) :
306
306
  new Vector2(x.x, x.y);
307
307
  }
@@ -330,13 +330,19 @@ class Vector2
330
330
  * @param {Number} [y] - Y axis location */
331
331
  constructor(x=0, y=0)
332
332
  {
333
- ASSERT(typeof x === 'number' && typeof y === 'number');
333
+ ASSERT(typeof x == 'number' && typeof y == 'number');
334
334
  /** @property {Number} - X axis location */
335
335
  this.x = x;
336
336
  /** @property {Number} - Y axis location */
337
337
  this.y = y;
338
338
  }
339
339
 
340
+ /** Sets values of this vector and returns self
341
+ * @param {Number} [x] - X axis location
342
+ * @param {Number} [y] - Y axis location
343
+ * @return {Vector2} */
344
+ set(x=0, y=0) { this.x=x; this.y=y; return this; }
345
+
340
346
  /** Returns a new vector that is a copy of this
341
347
  * @return {Vector2} */
342
348
  copy() { return new Vector2(this.x, this.y); }
@@ -588,6 +594,15 @@ class Color
588
594
  this.a = a;
589
595
  }
590
596
 
597
+ /** Sets values of this color and returns self
598
+ * @param {Number} [r] - red
599
+ * @param {Number} [g] - green
600
+ * @param {Number} [b] - blue
601
+ * @param {Number} [a] - alpha
602
+ * @return {Color} */
603
+ set(r=1, g=1, b=1, a=1)
604
+ { this.r=r; this.g=g; this.b=b; this.a=a; return this; }
605
+
591
606
  /** Returns a new color that is a copy of this
592
607
  * @return {Color} */
593
608
  copy() { return new Color(this.r, this.g, this.b, this.a); }
@@ -749,6 +764,64 @@ class Color
749
764
  }
750
765
  }
751
766
 
767
+ ///////////////////////////////////////////////////////////////////////////////
768
+ // default colors
769
+
770
+ /** Color - White
771
+ * @type {Color}
772
+ * @memberof Utilities */
773
+ const WHITE = rgb();
774
+
775
+ /** Color - Black
776
+ * @type {Color}
777
+ * @memberof Utilities */
778
+ const BLACK = rgb(0,0,0);
779
+
780
+ /** Color - Gray
781
+ * @type {Color}
782
+ * @memberof Utilities */
783
+ const GRAY = rgb(.5,.5,.5);
784
+
785
+ /** Color - Red
786
+ * @type {Color}
787
+ * @memberof Utilities */
788
+ const RED = rgb(1,0,0);
789
+
790
+ /** Color - Orange
791
+ * @type {Color}
792
+ * @memberof Utilities */
793
+ const ORANGE = rgb(1,.5,0);
794
+
795
+ /** Color - Yellow
796
+ * @type {Color}
797
+ * @memberof Utilities */
798
+ const YELLOW = rgb(1,1,0);
799
+
800
+ /** Color - Green
801
+ * @type {Color}
802
+ * @memberof Utilities */
803
+ const GREEN = rgb(0,1,0);
804
+
805
+ /** Color - Cyan
806
+ * @type {Color}
807
+ * @memberof Utilities */
808
+ const CYAN = rgb(0,1,1);
809
+
810
+ /** Color - Blue
811
+ * @type {Color}
812
+ * @memberof Utilities */
813
+ const BLUE = rgb(0,0,1);
814
+
815
+ /** Color - Purple
816
+ * @type {Color}
817
+ * @memberof Utilities */
818
+ const PURPLE = rgb(.5,0,1);
819
+
820
+ /** Color - Magenta
821
+ * @type {Color}
822
+ * @memberof Utilities */
823
+ const MAGENTA = rgb(1,0,1);
824
+
752
825
  ///////////////////////////////////////////////////////////////////////////////
753
826
 
754
827
  /**
@@ -87,7 +87,7 @@ function glPreRender()
87
87
 
88
88
  // clear and set to same size as main canvas
89
89
  glContext.viewport(0, 0, glCanvas.width=mainCanvas.width, glCanvas.height=mainCanvas.height);
90
- //glContext.clear(gl_COLOR_BUFFER_BIT); // auto cleared when size is set
90
+ glContext.clear(gl_COLOR_BUFFER_BIT);
91
91
 
92
92
  // set up the shader
93
93
  glContext.useProgram(glShader);
@@ -270,99 +270,6 @@ function glDraw(x, y, sizeX, sizeY, angle, uv0X, uv0Y, uv1X, uv1Y, rgba, rgbaAdd
270
270
  glInstanceCount++;
271
271
  }
272
272
 
273
- ///////////////////////////////////////////////////////////////////////////////
274
- // post processing - can be enabled to pass other canvases through a final shader
275
-
276
- let glPostShader, glPostTexture, glPostIncludeOverlay;
277
-
278
- /** Set up a post processing shader
279
- * @param {String} shaderCode
280
- * @param {Boolean} includeOverlay
281
- * @memberof WebGL */
282
- function glInitPostProcess(shaderCode, includeOverlay=false)
283
- {
284
- ASSERT(!glPostShader, 'can only have 1 post effects shader');
285
- if (headlessMode) return;
286
- if (!shaderCode) // default shader pass through
287
- shaderCode = 'void mainImage(out vec4 c,vec2 p){c=texture(iChannel0,p/iResolution.xy);}';
288
-
289
- // create the shader
290
- glPostShader = glCreateProgram(
291
- '#version 300 es\n' + // specify GLSL ES version
292
- 'precision highp float;'+ // use highp for better accuracy
293
- 'in vec2 p;'+ // position
294
- 'void main(){'+ // shader entry point
295
- 'gl_Position=vec4(p+p-1.,1,1);'+ // set position
296
- '}' // end of shader
297
- ,
298
- '#version 300 es\n' + // specify GLSL ES version
299
- 'precision highp float;'+ // use highp for better accuracy
300
- 'uniform sampler2D iChannel0;'+ // input texture
301
- 'uniform vec3 iResolution;'+ // size of output texture
302
- 'uniform float iTime;'+ // time
303
- 'out vec4 c;'+ // out color
304
- '\n' + shaderCode + '\n'+ // insert custom shader code
305
- 'void main(){'+ // shader entry point
306
- 'mainImage(c,gl_FragCoord.xy);'+ // call post process function
307
- 'c.a=1.;'+ // always use full alpha
308
- '}' // end of shader
309
- );
310
-
311
- // create buffer and texture
312
- glPostTexture = glCreateTexture(undefined);
313
- glPostIncludeOverlay = includeOverlay;
314
-
315
- // hide the original 2d canvas
316
- mainCanvas.style.visibility = 'hidden';
317
- if (glPostIncludeOverlay)
318
- overlayCanvas.style.visibility = 'hidden';
319
- }
320
-
321
- // Render the post processing shader, called automatically by the engine
322
- function glRenderPostProcess()
323
- {
324
- if (!glPostShader || headlessMode) return;
325
-
326
- // prepare to render post process shader
327
- if (glEnable)
328
- {
329
- glFlush(); // clear out the buffer
330
- mainContext.drawImage(glCanvas, 0, 0); // copy to the main canvas
331
- }
332
- else
333
- {
334
- // set the viewport
335
- glContext.viewport(0, 0, glCanvas.width = mainCanvas.width, glCanvas.height = mainCanvas.height);
336
- }
337
-
338
- // copy overlay canvas so it will be included in post processing
339
- glPostIncludeOverlay && mainContext.drawImage(overlayCanvas, 0, 0);
340
-
341
- // setup shader program to draw one triangle
342
- glContext.useProgram(glPostShader);
343
- glContext.bindBuffer(gl_ARRAY_BUFFER, glGeometryBuffer);
344
- glContext.pixelStorei(gl_UNPACK_FLIP_Y_WEBGL, 1);
345
- glContext.disable(gl_BLEND);
346
-
347
- // set textures, pass in the 2d canvas and gl canvas in separate texture channels
348
- glContext.activeTexture(gl_TEXTURE0);
349
- glContext.bindTexture(gl_TEXTURE_2D, glPostTexture);
350
- glContext.texImage2D(gl_TEXTURE_2D, 0, gl_RGBA, gl_RGBA, gl_UNSIGNED_BYTE, mainCanvas);
351
-
352
- // set vertex position attribute
353
- const vertexByteStride = 8;
354
- const pLocation = glContext.getAttribLocation(glPostShader, 'p');
355
- glContext.enableVertexAttribArray(pLocation);
356
- glContext.vertexAttribPointer(pLocation, 2, gl_FLOAT, false, vertexByteStride, 0);
357
-
358
- // set uniforms and draw
359
- const uniformLocation = (name)=>glContext.getUniformLocation(glPostShader, name);
360
- glContext.uniform1i(uniformLocation('iChannel0'), 0);
361
- glContext.uniform1f(uniformLocation('iTime'), time);
362
- glContext.uniform3f(uniformLocation('iResolution'), mainCanvas.width, mainCanvas.height, 1);
363
- glContext.drawArrays(gl_TRIANGLE_STRIP, 0, 4);
364
- }
365
-
366
273
  ///////////////////////////////////////////////////////////////////////////////
367
274
  // store gl constants as integers so their name doesn't use space in minifed
368
275
  const