littlejsengine 1.0.14 → 1.1.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.
Files changed (99) hide show
  1. package/build.bat +5 -1
  2. package/docs/Audio.html +2268 -0
  3. package/docs/Color.html +2989 -0
  4. package/docs/Debug.html +2933 -0
  5. package/docs/Draw.html +4092 -0
  6. package/docs/EngineObject.html +4458 -0
  7. package/docs/Input.html +2482 -0
  8. package/docs/Medal.html +1007 -0
  9. package/docs/Medals.html +628 -0
  10. package/docs/Music.html +584 -0
  11. package/docs/Newgrounds.html +1399 -0
  12. package/docs/Particle.html +4535 -0
  13. package/docs/ParticleEmitter.html +7770 -0
  14. package/docs/Random.html +1774 -0
  15. package/docs/Settings.html +2679 -0
  16. package/docs/Sound.html +1249 -0
  17. package/docs/TileCollision.html +1409 -0
  18. package/docs/TileLayer.html +6948 -0
  19. package/docs/TileLayerData.html +1052 -0
  20. package/docs/Timer.html +1183 -0
  21. package/docs/Utilities.html +3281 -0
  22. package/docs/Vector2.html +4207 -0
  23. package/docs/WebGL.html +2327 -0
  24. package/docs/engine.js.html +429 -0
  25. package/docs/engineAudio.js.html +609 -0
  26. package/docs/engineDebug.js.html +725 -0
  27. package/docs/engineDraw.js.html +419 -0
  28. package/docs/engineInput.js.html +401 -0
  29. package/docs/engineMedals.js.html +461 -0
  30. package/docs/engineObject.js.html +518 -0
  31. package/docs/engineParticles.js.html +441 -0
  32. package/docs/engineSettings.js.html +349 -0
  33. package/docs/engineTileLayer.js.html +513 -0
  34. package/docs/engineUtilities.js.html +647 -0
  35. package/docs/engineWebGL.js.html +522 -0
  36. package/docs/fonts/MavenPro-Regular.ttf +0 -0
  37. package/docs/fonts/Montserrat-Regular.ttf +0 -0
  38. package/docs/fonts/Muli-Black.ttf +0 -0
  39. package/docs/fonts/OFL-hind.txt +93 -0
  40. package/docs/fonts/OFL-montserrat.txt +93 -0
  41. package/docs/global.html +1691 -0
  42. package/docs/index.css +6 -0
  43. package/docs/index.html +247 -0
  44. package/docs/scripts/fix-code-block.js +53 -0
  45. package/docs/scripts/fix-navbar.js +25 -0
  46. package/docs/scripts/linenumber.js +25 -0
  47. package/docs/scripts/misc.js +217 -0
  48. package/docs/scripts/resize.js +85 -0
  49. package/docs/scripts/search.js +83 -0
  50. package/docs/scripts/third-party/Apache-License-2.0.txt +202 -0
  51. package/docs/scripts/third-party/fuse.js +9 -0
  52. package/docs/scripts/third-party/lang-css.js +2 -0
  53. package/docs/scripts/third-party/prettify.js +28 -0
  54. package/docs/static/favicon.png +0 -0
  55. package/docs/static/index.css +4 -0
  56. package/docs/styles/clean-jsdoc-theme-base.css +393 -0
  57. package/docs/styles/clean-jsdoc-theme-dark.css +324 -0
  58. package/docs/styles/clean-jsdoc-theme-light.css +319 -0
  59. package/docs/styles/reset.css +346 -0
  60. package/docs/styles/third-party/ionicons.min.css +11 -0
  61. package/docs/styles/third-party/prettify-jsdoc.css +111 -0
  62. package/docs/styles/third-party/prettify-tomorrow.css +5 -0
  63. package/engine/engine.all.js +1997 -744
  64. package/engine/engine.all.min.js +1 -1
  65. package/engine/engine.all.release.js +1743 -569
  66. package/engine/engine.js +107 -39
  67. package/engine/engineAudio.js +152 -34
  68. package/engine/{build.bat → engineBuild.bat} +10 -10
  69. package/engine/{buildSetup.bat → engineBuildSetup.bat} +0 -0
  70. package/engine/engineDebug.js +105 -32
  71. package/engine/engineDraw.js +137 -32
  72. package/engine/engineInput.js +111 -37
  73. package/engine/{engineMedal.js → engineMedals.js} +129 -56
  74. package/engine/engineObject.js +117 -67
  75. package/engine/engineParticles.js +282 -0
  76. package/engine/engineRelease.js +3 -9
  77. package/engine/engineSettings.js +190 -0
  78. package/engine/engineTileLayer.js +147 -44
  79. package/engine/engineUtilities.js +488 -0
  80. package/engine/engineWebGL.js +113 -74
  81. package/examples/breakout/game.js +1 -1
  82. package/examples/breakout/gameObjects.js +3 -3
  83. package/examples/breakout/index.html +3 -3
  84. package/examples/platformer/game.js +5 -7
  85. package/examples/platformer/gameEffects.js +7 -7
  86. package/examples/platformer/gameLevel.js +2 -2
  87. package/examples/platformer/gameObjects.js +4 -5
  88. package/examples/platformer/gamePlayer.js +3 -3
  89. package/examples/platformer/index.html +6 -6
  90. package/examples/puzzle/game.js +1 -2
  91. package/examples/puzzle/index.html +2 -2
  92. package/examples/stress/index.html +2 -8
  93. package/game.js +5 -11
  94. package/index.html +13 -13
  95. package/package.json +4 -4
  96. package/tiles.png +0 -0
  97. package/engine/engineConfig.js +0 -64
  98. package/engine/engineParticle.js +0 -199
  99. package/engine/engineUtil.js +0 -147
@@ -1,24 +1,61 @@
1
- /*
2
- LittleJS Medal System
3
- - Tracks and displays medals
4
- - Saves medals to local storage
5
- - Newgrounds and OS13k integration
6
- */
1
+ /**
2
+ * LittleJS Medal System
3
+ * <br> - Tracks and displays medals
4
+ * <br> - Saves medals to local storage
5
+ * <br> - Newgrounds and OS13k integration
6
+ * @namespace Medals
7
+ */
7
8
 
8
9
  'use strict';
9
10
 
10
- const medals = [], medalsDisplayQueue = [];
11
- let medalsGameName, medalsPreventUnlock, medalsDisplayTimer, newgrounds;
11
+ /** List of all medals
12
+ * @memberof Medals */
13
+ const medals = [];
12
14
 
13
- function medalsInit(gameName)
15
+ /** Set to stop medals from being unlockable (like if cheats are enabled)
16
+ * @memberof Medals */
17
+ let medalsPreventUnlock;
18
+
19
+ /** This can used to enable Newgrounds functionality
20
+ * @type {Newgrounds}
21
+ * @memberof Medals */
22
+ let newgrounds;
23
+
24
+ // Engine internal variables not exposed to documentation
25
+ let medalsDisplayQueue = [], medalsSaveName, medalsDisplayTimer;
26
+
27
+ ///////////////////////////////////////////////////////////////////////////////
28
+
29
+ /** Initialize medals with a save name used for storage
30
+ * <br> - Checks if medals are unlocked
31
+ * <br> - Call this after creating all medals
32
+ * @param {String} saveName
33
+ * @memberof Medals */
34
+ function medalsInit(saveName)
14
35
  {
15
36
  // check if medals are unlocked
16
- medalsGameName = gameName;
37
+ medalsSaveName = saveName;
17
38
  debugMedals || medals.forEach(medal=> medal.unlocked = localStorage[medal.storageKey()]);
18
39
  }
19
40
 
41
+ /**
42
+ * Medal Object - Tracks an unlockable medal
43
+ * @example
44
+ * // create a medal
45
+ * const medal_example = new Medal(0, 'Example Medal', 'More info about the medal goes here.', '🎖️');
46
+ *
47
+ * // unlock the medal
48
+ * medal_example.unlock();
49
+ */
20
50
  class Medal
21
51
  {
52
+ /** Create an medal object and adds it to the list of medals
53
+ * @param {Number} id - The unique identifier of the medal
54
+ * @param {String} name - Name of the medal
55
+ * @param {String} [description] - Description of the medal
56
+ * @param {String} [icon='🏆'] - Icon for the medal
57
+ * @param {String} [src] - Image location for the medal
58
+ */
22
59
  constructor(id, name, description='', icon='🏆', src)
23
60
  {
24
61
  ASSERT(id >= 0 && !medals[id]);
@@ -37,26 +74,25 @@ class Medal
37
74
  }
38
75
  }
39
76
 
77
+ /** Unlocks a medal if not already unlocked */
40
78
  unlock()
41
79
  {
42
80
  if (medalsPreventUnlock || this.unlocked)
43
81
  return;
44
82
 
45
83
  // save the medal
46
- ASSERT(medalsGameName); // game name must be set
84
+ ASSERT(medalsSaveName); // game name must be set
47
85
  localStorage[this.storageKey()] = this.unlocked = 1;
48
86
  medalsDisplayQueue.push(this);
49
87
 
50
88
  // save for newgrounds and OS13K
51
89
  newgrounds && newgrounds.unlockMedal(this.id);
52
- localStorage['OS13kTrophy,' + this.icon + ',' + medalsGameName + ',' + this.name] = this.description;
53
- }
54
-
55
- storageKey()
56
- {
57
- return medalsGameName + '_medal_' + this.id;
90
+ localStorage['OS13kTrophy,' + this.icon + ',' + medalsSaveName + ',' + this.name] = this.description;
58
91
  }
59
92
 
93
+ /** Render a medal
94
+ * @param {Number} [hidePercent=0] - How much to slide the medal off screen
95
+ */
60
96
  render(hidePercent=0)
61
97
  {
62
98
  const context = overlayContext;
@@ -77,25 +113,36 @@ class Medal
77
113
 
78
114
  // draw the text
79
115
  context.textAlign = 'left';
80
- context.font = '3em '+ defaultFont;
116
+ context.font = '3em '+ fontDefault;
81
117
  context.fillText(this.name, x+medalDisplayIconSize+25, y+35);
82
- context.font = '1.5em '+ defaultFont;
118
+ context.font = '1.5em '+ fontDefault;
83
119
  context.restore(context.fillText(this.description, x+medalDisplayIconSize+25, y+70));
84
120
  }
85
121
 
122
+ /** Render the icon for a medal
123
+ * @param {Number} x - Screen space X position
124
+ * @param {Number} y - Screen space Y position
125
+ * @param {Number} [size=medalDisplayIconSize] - Screen space size
126
+ */
86
127
  renderIcon(x, y, size=medalDisplayIconSize)
87
128
  {
88
129
  // draw the image or icon
89
130
  const context = overlayContext;
90
131
  context.textAlign = 'center';
91
132
  context.textBaseline = 'middle';
92
- context.font = size*.6 + 'px '+ defaultFont;
133
+ context.font = size*.6 + 'px '+ fontDefault;
93
134
  context.fillStyle = '#000';
94
135
  if (this.image)
95
136
  context.drawImage(this.image, x-size/2, y-size/2, size, size);
96
137
  else
97
138
  context.fillText(this.icon, x, y); // show icon if there is no image
98
139
  }
140
+
141
+ // Get local storage key used by the medal
142
+ storageKey()
143
+ {
144
+ return medalsSaveName + '_medal_' + this.id;
145
+ }
99
146
  }
100
147
 
101
148
  // engine automatically renders medals
@@ -106,9 +153,9 @@ function medalsRender()
106
153
 
107
154
  // update first medal in queue
108
155
  const medal = medalsDisplayQueue[0];
109
- const time = realTime - medalsDisplayTimer;
156
+ const time = timeReal - medalsDisplayTimer;
110
157
  if (!medalsDisplayTimer)
111
- medalsDisplayTimer = realTime;
158
+ medalsDisplayTimer = timeReal;
112
159
  else if (time > medalDisplayTime)
113
160
  medalsDisplayQueue.shift(medalsDisplayTimer = 0);
114
161
  else
@@ -123,15 +170,26 @@ function medalsRender()
123
170
  }
124
171
 
125
172
  ///////////////////////////////////////////////////////////////////////////////
126
- // Newgrounds API wrapper
127
173
 
174
+ /**
175
+ * Newgrounds API wrapper object
176
+ * @example
177
+ * // create a newgrounds object, replace the app id and cipher with your own
178
+ * const app_id = '53123:1ZuSTQ9l';
179
+ * const cipher = 'enF0vGH@Mj/FRASKL23Q==';
180
+ * newgrounds = new Newgrounds(app_id, cipher);
181
+ */
128
182
  class Newgrounds
129
183
  {
184
+ /** Create a newgrounds object
185
+ * @param {Number} app_id - The newgrounds App ID
186
+ * @param {String} [cipher] - The encryption Key (AES-128/Base64) */
130
187
  constructor(app_id, cipher)
131
188
  {
132
189
  ASSERT(!newgrounds && app_id);
133
190
  this.app_id = app_id;
134
191
  this.cipher = cipher;
192
+ this.host = location ? location.hostname : '';
135
193
 
136
194
  // create an instance of CryptoJS for encrypted calls
137
195
  cipher && (this.cryptoJS = CryptoJS());
@@ -153,11 +211,16 @@ class Newgrounds
153
211
  if (medal)
154
212
  {
155
213
  // copy newgrounds medal data
156
- medal.name = newgroundsMedal['name'];
214
+ medal.image = new Image();
215
+ medal.image.src = newgroundsMedal['icon'];
216
+ medal.name = newgroundsMedal['name'];
157
217
  medal.description = newgroundsMedal['description'];
158
- medal.unlocked = newgroundsMedal['unlocked'];
159
- medal.image = new Image();
160
- medal.image.src = newgroundsMedal['icon'];
218
+ medal.unlocked = newgroundsMedal['unlocked'];
219
+ medal.difficulty = newgroundsMedal['difficulty'];
220
+ medal.value = newgroundsMedal['value'];
221
+
222
+ if (medal.value)
223
+ medal.description = medal.description + ' (' + medal.value + ')';
161
224
  }
162
225
  }
163
226
 
@@ -167,30 +230,55 @@ class Newgrounds
167
230
  debugMedals && console.log(this.scoreboards);
168
231
  }
169
232
 
170
- unlockMedal(id)
171
- {
172
- return this.call('Medal.unlock', {'id':id}, 1);
173
- }
233
+ /** Send message to unlock a medal by id
234
+ * @param {Number} id - The medal id */
235
+ unlockMedal(id) { return this.call('Medal.unlock', {'id':id}, 1); }
174
236
 
175
- postScore(id, value)
176
- {
177
- return this.call('ScoreBoard.postScore', {'id':id, 'value':value}, 1);
178
- }
237
+ /** Send message to post score
238
+ * @param {Number} id - The scoreboard id
239
+ * @param {Number} value - The score value */
240
+ postScore(id, value) { return this.call('ScoreBoard.postScore', {'id':id, 'value':value}, 1); }
241
+
242
+ /** Send message to log a view */
243
+ logView() { return this.call('App.logView', {'host':this.host}, 1); }
179
244
 
245
+ /** Get scores from a scoreboard
246
+ * @param {Number} id - The scoreboard id
247
+ * @param {String} [user=0] - A user's id or name
248
+ * @param {Number} [social=0] - If true, only social scores will be loaded
249
+ * @param {Number} [skip=0] - Number of scores to skip before start
250
+ * @param {Number} [limit=10] - Number of scores to include in the list
251
+ * @return {Object} - The response JSON object
252
+ */
180
253
  getScores(id, user=0, social=0, skip=0, limit=10)
181
- {
182
- return this.call('ScoreBoard.getScores',
183
- {'id':id, 'user':user, 'social':social, 'skip':skip, 'limit':limit});
184
- }
185
-
254
+ { return this.call('ScoreBoard.getScores', {'id':id, 'user':user, 'social':social, 'skip':skip, 'limit':limit}); }
255
+
256
+ /** Send a message to call a component of the Newgrounds API
257
+ * @param {String} component - Name of the component
258
+ * @param {Object} [parameters=0] - Parameters to use for call
259
+ * @param {Boolean} [async=0] - If true, wait for response before continuing (will cause stall)
260
+ * @return {Object} - The response JSON object
261
+ */
186
262
  call(component, parameters=0, async=0)
187
263
  {
264
+ const call = {'component':component, 'parameters':parameters};
265
+ if (this.cipher)
266
+ {
267
+ // encrypt using AES-128 Base64 with cryptoJS
268
+ const cryptoJS = this.cryptoJS;
269
+ const aesKey = cryptoJS['enc']['Base64']['parse'](this.cipher);
270
+ const iv = cryptoJS['lib']['WordArray']['random'](16);
271
+ const encrypted = cryptoJS['AES']['encrypt'](JSON.stringify(call), aesKey, {'iv':iv});
272
+ call['secure'] = cryptoJS['enc']['Base64']['stringify'](iv.concat(encrypted['ciphertext']));
273
+ call['parameters'] = 0;
274
+ }
275
+
188
276
  // build the input object
189
277
  const input =
190
278
  {
191
279
  'app_id': this.app_id,
192
280
  'session_id': this.session_id,
193
- 'call': this.encryptCall({'component':component, 'parameters':parameters})
281
+ 'call': call
194
282
  };
195
283
 
196
284
  // build post data
@@ -205,21 +293,6 @@ class Newgrounds
205
293
  debugMedals && console.log(xmlHttp.responseText);
206
294
  return xmlHttp.responseText && JSON.parse(xmlHttp.responseText);
207
295
  }
208
-
209
- encryptCall(call)
210
- {
211
- if (!this.cipher)
212
- return call;
213
-
214
- // encrypt using AES-128 Base64 with cryptoJS
215
- const cryptoJS = this.cryptoJS;
216
- const aesKey = cryptoJS['enc']['Base64']['parse'](this.cipher);
217
- const iv = cryptoJS['lib']['WordArray']['random'](16);
218
- const encrypted = cryptoJS['AES']['encrypt'](JSON.stringify(call), aesKey, {'iv':iv});
219
- call['secure'] = cryptoJS['enc']['Base64']['stringify'](iv.concat(encrypted['ciphertext']));
220
- call['parameters'] = 0;
221
- return call;
222
- }
223
296
  }
224
297
 
225
298
  ///////////////////////////////////////////////////////////////////////////////
@@ -1,49 +1,86 @@
1
1
  /*
2
- LittleJS Object Base Class
3
- - Base object class used by the engine
4
- - Automatically adds self to object list
5
- - Will be updated and rendered each frame
6
- - Renders as a sprite from a tilesheet by default
7
- - Can have color and addtive color applied
8
- - 2d Physics and collision system
9
- - Sorted by renderOrder
10
- - Objects can have children attached
11
- - Parents are updated before children, and set child transform
12
- - Call destroy() to get rid of objects
2
+ LittleJS Object System
13
3
  */
14
4
 
15
5
  'use strict';
16
6
 
7
+ /**
8
+ * LittleJS Object Base Object Class
9
+ * <br> - Base object class used by the engine
10
+ * <br> - Automatically adds self to object list
11
+ * <br> - Will be updated and rendered each frame
12
+ * <br> - Renders as a sprite from a tilesheet by default
13
+ * <br> - Can have color and addtive color applied
14
+ * <br> - 2d Physics and collision system
15
+ * <br> - Sorted by renderOrder
16
+ * <br> - Objects can have children attached
17
+ * <br> - Parents are updated before children, and set child transform
18
+ * <br> - Call destroy() to get rid of objects
19
+ * @example
20
+ * // create an engine object, normally you would first extend the class with your own
21
+ * const pos = vec2(2,3);
22
+ * const object = new EngineObject(pos);
23
+ */
17
24
  class EngineObject
18
25
  {
19
- constructor(pos, size=defaultObjectSize, tileIndex=-1, tileSize=defaultTileSize, angle=0, color)
26
+ /** Create an engine object and adds it to the list of objects
27
+ * @param {Vector2} [position=new Vector2(0,0)] - World space position of the object
28
+ * @param {Vector2} [size=objectDefaultSize] - World space size of the object
29
+ * @param {Number} [tileIndex=-1] - Tile to use to render object, untextured if -1
30
+ * @param {Vector2} [tileSize=tileSizeDefault] - Size of tile in source pixels
31
+ * @param {Number} [angle=0] - Angle to rotate the object
32
+ * @param {Color} [color] - Color to apply to tile when rendered
33
+ * @param {Number} [renderOrder=0] - Objects sorted by renderOrder before being rendered
34
+ */
35
+ constructor(pos=vec2(), size=objectDefaultSize, tileIndex=-1, tileSize=tileSizeDefault, angle=0, color, renderOrder=0)
20
36
  {
21
37
  // set passed in params
22
38
  ASSERT(pos && pos.x != undefined && size.x != undefined); // ensure pos and size are vec2s
39
+
40
+ /** @property {Vector2} - World space position of the object */
23
41
  this.pos = pos.copy();
42
+ /** @property {Vector2} - World space width and height of the object */
24
43
  this.size = size;
44
+ /** @property {Vector2} - Size of object used for drawing, uses size if not set */
45
+ this.drawSize;
46
+ /** @property {Number} - Tile to use to render object, untextured if -1 */
25
47
  this.tileIndex = tileIndex;
48
+ /** @property {Vector2} - Size of tile in source pixels */
26
49
  this.tileSize = tileSize;
50
+ /** @property {Number} - Angle to rotate the object */
27
51
  this.angle = angle;
52
+ /** @property {Color} - Color to apply when rendered */
28
53
  this.color = color;
54
+ /** @property {Color} - Additive color to apply when rendered */
55
+ this.additiveColor;
29
56
 
30
- // set physics defaults
31
- this.mass = defaultObjectMass;
32
- this.damping = defaultObjectDamping;
33
- this.angleDamping = defaultObjectAngleDamping;
34
- this.elasticity = defaultObjectElasticity;
35
- this.friction = defaultObjectFriction;
57
+ // set object defaults
58
+ /** @property {Number} [mass=objectDefaultMass] - How heavy the object is */
59
+ this.mass = objectDefaultMass;
60
+ /** @property {Number} [damping=objectDefaultDamping] - How much to slow down velocity each frame (0-1) */
61
+ this.damping = objectDefaultDamping;
62
+ /** @property {Number} [angleDamping=objectDefaultAngleDamping] - How much to slow down rotation each frame (0-1) */
63
+ this.angleDamping = objectDefaultAngleDamping;
64
+ /** @property {Number} [elasticity=objectDefaultElasticity] - How bouncy the object is when colliding (0-1) */
65
+ this.elasticity = objectDefaultElasticity;
66
+ /** @property {Number} [friction=objectDefaultFriction] - How much friction to apply when sliding (0-1) */
67
+ this.friction = objectDefaultFriction;
68
+ /** @property {Number} [gravityScale=1] - How much to scale gravity by for this object */
69
+ this.gravityScale = 1;
70
+ /** @property {Number} [renderOrder=0] - Objects are sorted by render order */
71
+ this.renderOrder = renderOrder;
36
72
 
37
- // init other object stuff
73
+ // init other internal object stuff
38
74
  this.spawnTime = time;
39
- this.velocity = vec2(this.collideSolidObjects = this.renderOrder = this.angleVelocity = 0);
40
- this.collideTiles = this.gravityScale = 1;
75
+ this.velocity = vec2(this.collideSolidObjects = this.angleVelocity = 0);
76
+ this.collideTiles = 1;
41
77
  this.children = [];
42
78
 
43
79
  // add to list of objects
44
80
  engineObjects.push(this);
45
81
  }
46
82
 
83
+ /** Update the object transform and physics, called automatically by engine once each frame */
47
84
  update()
48
85
  {
49
86
  const parent = this.parent;
@@ -56,8 +93,8 @@ class EngineObject
56
93
  }
57
94
 
58
95
  // limit max speed to prevent missing collisions
59
- this.velocity.x = clamp(this.velocity.x, maxObjectSpeed, -maxObjectSpeed);
60
- this.velocity.y = clamp(this.velocity.y, maxObjectSpeed, -maxObjectSpeed);
96
+ this.velocity.x = clamp(this.velocity.x, objectMaxSpeed, -objectMaxSpeed);
97
+ this.velocity.y = clamp(this.velocity.y, objectMaxSpeed, -objectMaxSpeed);
61
98
 
62
99
  // apply physics
63
100
  const oldPos = this.pos.copy();
@@ -111,7 +148,7 @@ class EngineObject
111
148
  if (o.mass) // push away if not fixed
112
149
  o.velocity = o.velocity.subtract(velocity);
113
150
 
114
- debugPhysics && debugAABB(this.pos, o.pos, this.size, o.size, '#f00');
151
+ debugPhysics && debugAABB(this.pos, this.size, o.pos, o.size, '#f00');
115
152
  continue;
116
153
  }
117
154
 
@@ -177,7 +214,7 @@ class EngineObject
177
214
  this.velocity.x *= -this.elasticity;
178
215
  }
179
216
 
180
- debugPhysics && debugAABB(this.pos, o.pos, this.size, o.size, '#f0f');
217
+ debugPhysics && debugAABB(this.pos, this.size, o.pos, o.size, '#f0f');
181
218
  }
182
219
  }
183
220
  if (this.collideTiles)
@@ -185,8 +222,6 @@ class EngineObject
185
222
  // check collision against tiles
186
223
  if (tileCollisionTest(this.pos, this.size, this))
187
224
  {
188
- //debugPhysics && debugRect(this.pos, this.size, '#ff0');
189
-
190
225
  // if already was stuck in collision, don't do anything
191
226
  // this should not happen unless something starts in collision
192
227
  if (!tileCollisionTest(oldPos, this.size, this))
@@ -199,13 +234,20 @@ class EngineObject
199
234
  // set if landed on ground
200
235
  this.groundObject = wasMovingDown;
201
236
 
202
- // push out of collision and bounce
203
- this.pos.y = oldPos.y;
237
+ // bounce velocity
204
238
  this.velocity.y *= -this.elasticity;
239
+
240
+ // adjust next velocity to settle on ground
241
+ const o = (oldPos.y - this.size.y/2|0) - (oldPos.y - this.size.y/2);
242
+ if (o < 0 && o > -1 && o > this.damping * this.velocity.y + gravity * this.gravityScale)
243
+ this.velocity.y = this.damping ? (o - gravity * this.gravityScale) / this.damping : 0;
244
+
245
+ // move to previous position
246
+ this.pos.y = oldPos.y;
205
247
  }
206
248
  if (isBlockedX)
207
249
  {
208
- // push out of collision and bounce
250
+ // move to previous position and bounce
209
251
  this.pos.x = oldPos.x;
210
252
  this.velocity.x *= -this.elasticity;
211
253
  }
@@ -214,12 +256,14 @@ class EngineObject
214
256
  }
215
257
  }
216
258
 
259
+ /** Render the object, draws a tile by default, automatically called each frame, sorted by renderOrder */
217
260
  render()
218
261
  {
219
262
  // default object render
220
263
  drawTile(this.pos, this.drawSize || this.size, this.tileIndex, this.tileSize, this.color, this.angle, this.mirror, this.additiveColor);
221
264
  }
222
265
 
266
+ /** Destroy this object, destroy it's children, detach it's parent, and mark it for removal */
223
267
  destroy()
224
268
  {
225
269
  if (this.destroyed)
@@ -231,15 +275,45 @@ class EngineObject
231
275
  for (const child of this.children)
232
276
  child.destroy(child.parent = 0);
233
277
  }
234
- collideWithTile(data, pos) { return data > 0; }
235
- collideWithTileRaycast(data, pos) { return data > 0; }
278
+
279
+ /** Called to check if a tile collision should be resolved
280
+ * @param {Number} tileData - the value of the tile at the position
281
+ * @param {Vector2} pos - tile where the collision occured
282
+ * @return {Boolean} true if the collision should be resolved */
283
+ collideWithTile(tileData, pos) { return tileData > 0; }
284
+
285
+ /** Called to check if a tile raycast hit
286
+ * @param {Number} tileData - the value of the tile at the position
287
+ * @param {Vector2} pos - tile where the raycast is
288
+ * @return {Boolean} true if the raycast should hit */
289
+ collideWithTileRaycast(tileData, pos) { return tileData > 0; }
290
+
291
+ /** Called to check if a tile raycast hit
292
+ * @param {EngineObject} object - the object to test against
293
+ * @return {Boolean} true if the collision should be resolved
294
+ */
236
295
  collideWithObject(o) { return 1; }
296
+
297
+ /** How long since the object was created
298
+ * @return {Number} */
237
299
  getAliveTime() { return time - this.spawnTime; }
238
- applyAcceleration(a) { ASSERT(!this.isFixed()); this.velocity = this.velocity.add(a); }
300
+
301
+ /** Apply acceleration to this object (adjust velocity, not affected by mass)
302
+ * @param {Vector2} acceleration */
303
+ applyAcceleration(a) { if (this.mass) this.velocity = this.velocity.add(a); }
304
+
305
+ /** Apply force to this object (adjust velocity, affected by mass)
306
+ * @param {Vector2} force */
239
307
  applyForce(force) { this.applyAcceleration(force.scale(1/this.mass)); }
240
- isFixed() { return !this.mass; }
241
- getMirrorSign(s=1) { return this.mirror ? -s : s; }
308
+
309
+ /** Get the direction of the mirror
310
+ * @return {Number} -1 if this.mirror is true, or 1 if not mirrored */
311
+ getMirrorSign() { return this.mirror ? -1 : 1; }
242
312
 
313
+ /** Attaches a child to this with a given local transform
314
+ * @param {EngineObject} child
315
+ * @param {Vector2} [localPos=new Vector2]
316
+ * @param {Number} [localAngle=0] */
243
317
  addChild(child, localPos=vec2(), localAngle=0)
244
318
  {
245
319
  ASSERT(!child.parent && !this.children.includes(child));
@@ -248,6 +322,9 @@ class EngineObject
248
322
  child.localPos = localPos.copy();
249
323
  child.localAngle = localAngle;
250
324
  }
325
+
326
+ /** Removes a child from this one
327
+ * @param {EngineObject} child */
251
328
  removeChild(child)
252
329
  {
253
330
  ASSERT(child.parent == this && this.children.includes(child));
@@ -255,7 +332,11 @@ class EngineObject
255
332
  child.parent = 0;
256
333
  }
257
334
 
258
- setCollision(collideSolidObjects, isSolid, collideTiles=1)
335
+ /** Set how this object collides
336
+ * @param {boolean} [collideSolidObjects=0] - Does it collide with solid objects
337
+ * @param {boolean} [isSolid=0] - Does it collide with and block other objects (expensive in large numbers)
338
+ * @param {boolean} [collideTiles=1] - Does it collide with the tile collision */
339
+ setCollision(collideSolidObjects=0, isSolid=0, collideTiles=1)
259
340
  {
260
341
  ASSERT(collideSolidObjects || !isSolid); // solid objects must be set to collide
261
342
 
@@ -275,35 +356,4 @@ class EngineObject
275
356
  this.isSolid = isSolid;
276
357
  this.collideTiles = collideTiles;
277
358
  }
278
- }
279
-
280
- function destroyAllObjects()
281
- {
282
- // remove all objects that are not persistent or are descendants of something persistent
283
- for (const o of engineObjects)
284
- o.persistent || o.parent || o.destroy();
285
- engineObjects = engineObjects.filter(o=>!o.destroyed);
286
- }
287
-
288
- function forEachObject(pos, size, callbackFunction, objects=engineObjects)
289
- {
290
- if (!pos)
291
- {
292
- // all objects
293
- for (const o of objects)
294
- callbackFunction(o);
295
- }
296
- else if (size.x != undefined)
297
- {
298
- // aabb test
299
- for (const o of objects)
300
- isOverlapping(pos, size, o.pos, o.size) && callbackFunction(o);
301
- }
302
- else
303
- {
304
- // circle test
305
- const sizeSquared = size*size;
306
- for (const o of objects)
307
- pos.distanceSquared(o.pos) < sizeSquared && callbackFunction(o);
308
- }
309
359
  }