littlejsengine 1.18.4 → 1.18.7

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 (177) hide show
  1. package/COPYRIGHT.txt +38 -0
  2. package/FAQ.md +633 -0
  3. package/README.md +19 -1
  4. package/dist/littlejs.d.ts +349 -54
  5. package/dist/littlejs.esm.js +1469 -448
  6. package/dist/littlejs.esm.min.js +1 -1
  7. package/dist/littlejs.js +1438 -436
  8. package/dist/littlejs.min.js +1 -1
  9. package/dist/littlejs.release.js +1438 -436
  10. package/package.json +8 -1
  11. package/plugins/box2d.js +32 -7
  12. package/{src/engineMedals.js → plugins/medalSystem.js} +248 -196
  13. package/plugins/pathFinder.js +758 -0
  14. package/plugins/pluginExport.js +19 -0
  15. package/plugins/tween.js +1 -1
  16. package/plugins/tweenSystem.js +509 -0
  17. package/plugins/uiSystem.js +150 -11
  18. package/src/engine.js +2 -141
  19. package/src/engineAudio.js +8 -5
  20. package/src/engineBuild.mjs +4 -2
  21. package/src/engineDebug.js +1 -1
  22. package/src/engineDraw.js +21 -3
  23. package/src/engineExport.js +12 -12
  24. package/src/engineInput.js +1 -1
  25. package/src/engineLogo.js +146 -0
  26. package/src/engineMath.js +10 -3
  27. package/src/engineObject.js +11 -5
  28. package/src/engineParticles.js +6 -3
  29. package/src/engineRelease.js +1 -1
  30. package/src/engineSettings.js +0 -47
  31. package/src/engineUtilities.js +7 -2
  32. package/src/engineWebGL.js +34 -7
  33. package/src/jsconfig.json +3 -1
  34. package/.github/workflows/test.yml +0 -17
  35. package/AI.md +0 -173
  36. package/CLAUDE.md +0 -1
  37. package/examples/box2d/game.js +0 -192
  38. package/examples/box2d/gameObjects.js +0 -564
  39. package/examples/box2d/index.html +0 -9
  40. package/examples/box2d/scenes.js +0 -194
  41. package/examples/box2d/tiles.png +0 -0
  42. package/examples/breakout/game.js +0 -175
  43. package/examples/breakout/gameObjects.js +0 -147
  44. package/examples/breakout/index.html +0 -8
  45. package/examples/breakout/tiles.png +0 -0
  46. package/examples/breakoutTutorial/README.md +0 -521
  47. package/examples/breakoutTutorial/game.js +0 -191
  48. package/examples/breakoutTutorial/images/1.png +0 -0
  49. package/examples/breakoutTutorial/images/10.png +0 -0
  50. package/examples/breakoutTutorial/images/11.png +0 -0
  51. package/examples/breakoutTutorial/images/2.png +0 -0
  52. package/examples/breakoutTutorial/images/3.png +0 -0
  53. package/examples/breakoutTutorial/images/4.png +0 -0
  54. package/examples/breakoutTutorial/images/5.png +0 -0
  55. package/examples/breakoutTutorial/images/6.png +0 -0
  56. package/examples/breakoutTutorial/images/7.png +0 -0
  57. package/examples/breakoutTutorial/images/8.png +0 -0
  58. package/examples/breakoutTutorial/images/9.png +0 -0
  59. package/examples/breakoutTutorial/index.html +0 -8
  60. package/examples/electron/build.mjs +0 -127
  61. package/examples/electron/electron.js +0 -35
  62. package/examples/electron/game.js +0 -54
  63. package/examples/electron/index.html +0 -13
  64. package/examples/electron/package.json +0 -5
  65. package/examples/electron/tiles.png +0 -0
  66. package/examples/empty/game.js +0 -51
  67. package/examples/empty/index.html +0 -5
  68. package/examples/empty/tiles.png +0 -0
  69. package/examples/favicon.png +0 -0
  70. package/examples/games.jpg +0 -0
  71. package/examples/htmlMenu/game.js +0 -82
  72. package/examples/htmlMenu/index.html +0 -45
  73. package/examples/htmlMenu/tiles.png +0 -0
  74. package/examples/index.html +0 -62
  75. package/examples/logo.png +0 -0
  76. package/examples/logo2.png +0 -0
  77. package/examples/module/build.mjs +0 -124
  78. package/examples/module/game.js +0 -132
  79. package/examples/module/index.html +0 -10
  80. package/examples/module/tiles.png +0 -0
  81. package/examples/particles/index.html +0 -426
  82. package/examples/particles/tiles.png +0 -0
  83. package/examples/platformer/data/gameLevelData.tmx +0 -143
  84. package/examples/platformer/data/gameLevelData.tsx +0 -4
  85. package/examples/platformer/game.js +0 -147
  86. package/examples/platformer/gameCharacter.js +0 -309
  87. package/examples/platformer/gameEffects.js +0 -278
  88. package/examples/platformer/gameLevel.js +0 -205
  89. package/examples/platformer/gameLevelData.json +0 -171
  90. package/examples/platformer/gameObjects.js +0 -378
  91. package/examples/platformer/gamePlayer.js +0 -35
  92. package/examples/platformer/index.html +0 -9
  93. package/examples/platformer/tiles.png +0 -0
  94. package/examples/platformer/tilesLevel.png +0 -0
  95. package/examples/puzzle/game.js +0 -331
  96. package/examples/puzzle/index.html +0 -8
  97. package/examples/puzzle/tiles.png +0 -0
  98. package/examples/screenshot.jpg +0 -0
  99. package/examples/shorts/animation.js +0 -18
  100. package/examples/shorts/base.html +0 -54
  101. package/examples/shorts/blending.js +0 -14
  102. package/examples/shorts/box2d.js +0 -51
  103. package/examples/shorts/box2dCar.js +0 -59
  104. package/examples/shorts/box2dPool.js +0 -107
  105. package/examples/shorts/box2dTileLayer.js +0 -48
  106. package/examples/shorts/cameraDrag.js +0 -22
  107. package/examples/shorts/clock.js +0 -21
  108. package/examples/shorts/colors.js +0 -25
  109. package/examples/shorts/debugDraw.js +0 -37
  110. package/examples/shorts/empty.js +0 -30
  111. package/examples/shorts/flappyGame.js +0 -55
  112. package/examples/shorts/fontImage.js +0 -17
  113. package/examples/shorts/fps.js +0 -90
  114. package/examples/shorts/helloWorld.js +0 -11
  115. package/examples/shorts/hillGlideGame.js +0 -63
  116. package/examples/shorts/input.js +0 -64
  117. package/examples/shorts/landerGame.js +0 -57
  118. package/examples/shorts/maze.js +0 -48
  119. package/examples/shorts/medals.js +0 -51
  120. package/examples/shorts/music.js +0 -78
  121. package/examples/shorts/musicPlayer.js +0 -137
  122. package/examples/shorts/nineSlice.js +0 -40
  123. package/examples/shorts/parallax.js +0 -72
  124. package/examples/shorts/particles.js +0 -28
  125. package/examples/shorts/piano.js +0 -44
  126. package/examples/shorts/platformer.js +0 -45
  127. package/examples/shorts/pongGame.js +0 -41
  128. package/examples/shorts/postProcess.js +0 -62
  129. package/examples/shorts/sequencer.js +0 -124
  130. package/examples/shorts/shader.js +0 -29
  131. package/examples/shorts/shapes.js +0 -21
  132. package/examples/shorts/slidingPuzzle.js +0 -52
  133. package/examples/shorts/song.mp3 +0 -0
  134. package/examples/shorts/sound.js +0 -36
  135. package/examples/shorts/spaceGame.js +0 -58
  136. package/examples/shorts/spriteAtlas.js +0 -31
  137. package/examples/shorts/starfield.js +0 -15
  138. package/examples/shorts/texture.js +0 -16
  139. package/examples/shorts/textureWrapped.js +0 -12
  140. package/examples/shorts/tileLayer.js +0 -48
  141. package/examples/shorts/tileRaycast.js +0 -39
  142. package/examples/shorts/tiles.png +0 -0
  143. package/examples/shorts/tiltedView.js +0 -63
  144. package/examples/shorts/timers.js +0 -53
  145. package/examples/shorts/topDown.js +0 -38
  146. package/examples/shorts/tween.js +0 -24
  147. package/examples/shorts/uiSystem.js +0 -61
  148. package/examples/shorts/video.webm +0 -0
  149. package/examples/shorts/videoPlayer.js +0 -34
  150. package/examples/shorts.js +0 -743
  151. package/examples/starter/build.bat +0 -7
  152. package/examples/starter/build.mjs +0 -126
  153. package/examples/starter/game.js +0 -137
  154. package/examples/starter/index.html +0 -35
  155. package/examples/starter/tiles.png +0 -0
  156. package/examples/stress/index.html +0 -173
  157. package/examples/style.css +0 -150
  158. package/examples/tweenSystem/game.js +0 -171
  159. package/examples/tweenSystem/index.html +0 -10
  160. package/examples/tweenSystem/tiles.png +0 -0
  161. package/examples/typescript/build.mjs +0 -60
  162. package/examples/typescript/game.js +0 -100
  163. package/examples/typescript/game.ts +0 -132
  164. package/examples/typescript/index.html +0 -10
  165. package/examples/typescript/tiles.png +0 -0
  166. package/examples/typescript/tsconfig.json +0 -17
  167. package/examples/uiSystem/game.js +0 -139
  168. package/examples/uiSystem/index.html +0 -10
  169. package/examples/uiSystem/tiles.png +0 -0
  170. package/jsconfig.json +0 -12
  171. package/plugins/desktop.ini +0 -2
  172. package/reference.md +0 -448
  173. package/test/math.test.mjs +0 -774
  174. package/test/setup.mjs +0 -22
  175. package/test/smoke.test.mjs +0 -274
  176. package/test/tween.test.mjs +0 -576
  177. package/test/util.test.mjs +0 -80
@@ -1,309 +0,0 @@
1
- /*
2
- LittleJS Platformer Example - Character
3
- - Platform style character
4
- - Handles animation and sound effects
5
- - Uses special platforming physics system
6
- - Characters can jump, shoot, roll, and throw grenades
7
- - Characters can climb ladders and break blocks
8
- - Characters also have a damage and can die
9
- - Has a weapon which can be fired
10
- */
11
-
12
- 'use strict';
13
-
14
- // import LittleJS module
15
- import * as LJS from '../../dist/littlejs.esm.js';
16
- import * as GameObjects from './gameObjects.js';
17
- import * as GameEffects from './gameEffects.js';
18
- import * as GameLevel from './gameLevel.js';
19
- import * as Game from './game.js';
20
- const {vec2, hsl, Timer} = LJS;
21
-
22
- export class Character extends GameObjects.GameObject
23
- {
24
- constructor(pos)
25
- {
26
- super(pos, vec2(.6,.95));
27
-
28
- this.lastPos = pos;
29
- this.groundTimer = new Timer;
30
- this.jumpTimer = new Timer;
31
- this.pressedJumpTimer = new Timer;
32
- this.dodgeTimer = new Timer;
33
- this.dodgeRechargeTimer = new Timer;
34
- this.deadTimer = new Timer;
35
- this.grenadeThrowTimer = new Timer;
36
- this.drawSize = vec2(1);
37
- this.moveInput = vec2();
38
- this.color = hsl(LJS.rand(),1,.7);
39
- this.renderOrder = 10;
40
- this.walkCyclePercent = 0;
41
- this.health = 1;
42
- this.climbingLadder = false;
43
- this.setCollision(true,false);
44
-
45
- // attach weapon
46
- this.weapon = new GameObjects.Weapon(pos);
47
- this.weapon.renderOrder = this.renderOrder + 1;
48
- this.addChild(this.weapon, vec2(this.size.x,0));
49
- }
50
-
51
- update()
52
- {
53
- this.gravityScale = 1; // reset to default gravity
54
-
55
- if (this.isDead())
56
- return super.update();
57
-
58
- // get move input
59
- const moveInput = this.moveInput.copy();
60
-
61
- // jump
62
- if (!this.holdingJump)
63
- this.pressedJumpTimer.unset();
64
- else if (!this.wasHoldingJump || this.climbingWall)
65
- this.pressedJumpTimer.set(.3);
66
- this.wasHoldingJump = this.holdingJump;
67
-
68
- // wall climb
69
- this.climbingWall = 0;
70
- if (moveInput.x && !this.velocity.x && this.velocity.y < 0)
71
- {
72
- this.velocity.y *=.8;
73
- this.climbingWall = 1;
74
- }
75
-
76
- if (this.dodgeTimer.active())
77
- {
78
- // update roll
79
- this.angle = this.getMirrorSign()*2*LJS.PI*this.dodgeTimer.getPercent();
80
- if (this.groundObject)
81
- this.velocity.x += this.getMirrorSign()*.1;
82
- }
83
- else
84
- {
85
- // not rolling
86
- this.angle = 0;
87
- if (this.pressedDodge && !this.dodgeRechargeTimer.active())
88
- {
89
- // start dodge
90
- this.dodgeTimer.set(.4);
91
- this.dodgeRechargeTimer.set(2);
92
- this.jumpTimer.unset();
93
- GameEffects.sound_dodge.play(this.pos);
94
-
95
- if (!this.groundObject && this.getAliveTime() > .2)
96
- this.velocity.y += .2;
97
- }
98
- if (this.pressingThrow && !this.wasPressingThrow && !this.grenadeThrowTimer.active())
99
- {
100
- // throw grenade
101
- const grenade = new GameObjects.Grenade(this.pos);
102
- const speed = vec2(this.getMirrorSign(),LJS.rand(.8,.7)).normalize(.2+LJS.rand(.02));
103
- grenade.velocity = this.velocity.add(speed);
104
- grenade.angleVelocity = this.getMirrorSign() * LJS.rand(.8,.5);
105
- GameEffects.sound_jump.play(this.pos);
106
- this.grenadeThrowTimer.set(1);
107
- }
108
- this.wasPressingThrow = this.pressingThrow;
109
- }
110
-
111
- // allow grabbing ladder at head or feet
112
- let touchingLadder = 0;
113
- for (let y=2;y--;)
114
- {
115
- const testPos = this.pos.add(vec2(0, y + .1*moveInput.y - this.size.y/2));
116
- const collisionData = LJS.tileCollisionGetData(testPos);
117
- touchingLadder ||= collisionData == GameLevel.tileType_ladder;
118
- }
119
- if (!touchingLadder)
120
- this.climbingLadder = false;
121
- else if (moveInput.y)
122
- this.climbingLadder = true;
123
-
124
- if (this.weapon) // update weapon trigger
125
- this.weapon.triggerIsDown = this.holdingShoot && !this.dodgeTimer.active();
126
-
127
- if (this.climbingLadder)
128
- {
129
- // update ladder
130
- this.gravityScale = this.climbingWall = this.groundObject = 0;
131
- this.jumpTimer.unset();
132
- this.groundTimer.unset();
133
- this.velocity = this.velocity.multiply(vec2(.85)).add(vec2(0,.02*moveInput.y));
134
-
135
- // pull towards ladder
136
- const delta = (this.pos.x|0)+.5 - this.pos.x;
137
- this.velocity.x += .02*delta*LJS.abs(moveInput.x ? 0:moveInput.y);
138
- moveInput.x *= .2;
139
-
140
- // exit ladder if ground is below
141
- this.climbingLadder = moveInput.y >= 0 || LJS.tileCollisionGetData(this.pos.subtract(vec2(0,1))) <= 0;
142
- }
143
- else
144
- {
145
- // update jumping and ground check
146
- if (this.groundObject || this.climbingWall)
147
- {
148
- if (!this.groundTimer.isSet())
149
- GameEffects.sound_walk.play(this.pos); // land sound
150
- this.groundTimer.set(.1);
151
- }
152
-
153
- if (this.groundTimer.active() && !this.dodgeTimer.active())
154
- {
155
- // is on ground
156
- if (this.pressedJumpTimer.active() && !this.jumpTimer.active())
157
- {
158
- // start jump
159
- if (this.climbingWall)
160
- this.velocity.y = .25;
161
- else
162
- {
163
- this.velocity.y = .15;
164
- this.jumpTimer.set(.2);
165
- }
166
- GameEffects.sound_jump.play(this.pos);
167
- }
168
- }
169
-
170
- if (this.jumpTimer.active() && !this.climbingWall)
171
- {
172
- // update variable height jump
173
- this.groundTimer.unset();
174
- if (this.holdingJump && this.velocity.y > 0 && this.jumpTimer.active())
175
- this.velocity.y += .017;
176
- }
177
-
178
- if (!this.groundObject)
179
- {
180
- // air control
181
- if (LJS.sign(moveInput.x) == LJS.sign(this.velocity.x))
182
- moveInput.x *= .1; // moving with velocity
183
- else
184
- moveInput.x *= .2; // moving against velocity (stopping)
185
-
186
- // slight extra gravity when moving down
187
- if (this.velocity.y < 0)
188
- this.velocity.y += LJS.gravity.y*.2;
189
- }
190
- }
191
-
192
- // apply movement acceleration and clamp
193
- const maxCharacterSpeed = .2;
194
- this.velocity.x = LJS.clamp(this.velocity.x + moveInput.x * .04, -maxCharacterSpeed, maxCharacterSpeed);
195
-
196
- // track last pos for ladder collision code
197
- this.lastPos = this.pos.copy();
198
-
199
- // call parent
200
- super.update();
201
-
202
- // update walk cycle
203
- const speed = this.getSpeed();
204
- if (this.climbingLadder || this.groundTimer.active() && !this.dodgeTimer.active())
205
- {
206
- this.walkCyclePercent += speed * .5;
207
- this.walkCyclePercent = speed > .01 ? LJS.mod(this.walkCyclePercent) : 0;
208
- }
209
- else
210
- this.walkCyclePercent = 0;
211
-
212
- // update walk sound
213
- this.walkSoundTime += speed;
214
- if (speed > .001 && ((this.climbingLadder || this.groundTimer.active()) && !this.dodgeTimer.active()))
215
- {
216
- if (this.walkSoundTime > 1)
217
- {
218
- this.walkSoundTime = this.walkSoundTime%1;
219
- GameEffects.sound_walk.play(this.pos);
220
- }
221
- }
222
- else
223
- this.walkSoundTime = 0;
224
-
225
- // update mirror
226
- if (moveInput.x && !this.dodgeTimer.active())
227
- this.mirror = moveInput.x < 0;
228
- }
229
-
230
- render()
231
- {
232
- // update animation
233
- const animationFrame = this.isDead() ? 0 :
234
- this.climbingLadder || this.groundTimer.active() ?
235
- 2*this.walkCyclePercent|0 : 1;
236
- this.tileInfo = Game.spriteAtlas.player.frame(animationFrame);
237
-
238
- let bodyPos = this.pos;
239
- if (!this.isDead())
240
- {
241
- // bounce pos with walk cycle
242
- bodyPos = bodyPos.add(vec2(0,.05*LJS.sin(this.walkCyclePercent*LJS.PI)));
243
-
244
- // make bottom flush
245
- bodyPos = bodyPos.add(vec2(0,(this.drawSize.y-this.size.y)/2));
246
- }
247
- LJS.drawTile(bodyPos, this.drawSize, this.tileInfo, this.color, this.angle, this.mirror);
248
- }
249
-
250
- damage(damage, damagingObject)
251
- {
252
- if (this.isDead() || this.getAliveTime() < 1 || this.dodgeTimer.active())
253
- return 0;
254
-
255
- GameEffects.makeBlood(damagingObject ? damagingObject.pos : this.pos);
256
- return super.damage(damage, damagingObject);
257
- }
258
-
259
- kill(damagingObject)
260
- {
261
- if (this.isDead())
262
- return;
263
-
264
- GameEffects.makeBlood(this.pos, 100);
265
- GameEffects.sound_die.play(this.pos);
266
-
267
- this.health = 0;
268
- this.deadTimer.set();
269
- this.size = this.size.scale(.5);
270
- const fallDirection = damagingObject ? LJS.sign(damagingObject.velocity.x) : LJS.randSign();
271
- this.angleVelocity = fallDirection*LJS.rand(.22,.14);
272
- this.angleDamping = .9;
273
- this.renderOrder = -1; // move to back layer
274
- if (this.weapon)
275
- this.weapon.destroy();
276
- }
277
-
278
- collideWithTile(data, pos)
279
- {
280
- if (!data)
281
- return false;
282
-
283
- if (data == GameLevel.tileType_ladder)
284
- {
285
- // handle ladder collisions
286
- if (pos.y + 1 > this.lastPos.y - this.size.y/2)
287
- return false;
288
-
289
- if (LJS.tileCollisionGetData(pos.add(vec2(0,1))) // above
290
- && !LJS.tileCollisionGetData(pos.add(vec2(1,0))) // left
291
- && !LJS.tileCollisionGetData(pos.add(vec2(1,0)))) // right
292
- return false; // don't collide if something above it and nothing to left or right
293
-
294
- // allow standing on top of ladders
295
- return !this.climbingLadder;
296
- }
297
-
298
- const d = pos.y - this.pos.y;
299
- if (!this.climbingLadder && this.velocity.y > .1 && d > 0 && d < this.size.y/2)
300
- if (GameEffects.destroyTile(pos))
301
- {
302
- // break blocks above
303
- this.velocity.y = 0;
304
- return false;
305
- }
306
-
307
- return true;
308
- }
309
- }
@@ -1,278 +0,0 @@
1
- /*
2
- LittleJS Platformer Example - Effects
3
- - Plays different particle effects which can be persistent
4
- - Destroys terrain and makes explosions
5
- - Outlines tiles based on neighbor types
6
- - Generates parallax background layers
7
- - Draws moving starfield with plants and suns
8
- - Tracks zzfx sound effects
9
- */
10
-
11
- 'use strict';
12
-
13
- // import LittleJS module
14
- import * as LJS from '../../dist/littlejs.esm.js';
15
- import * as GameLevel from './gameLevel.js';
16
- const {vec2, hsl, rgb} = LJS;
17
-
18
- ///////////////////////////////////////////////////////////////////////////////
19
- // sound effects
20
-
21
- export const sound_shoot = new LJS.Sound([,,90,,.01,.03,4,,,,,,,9,50,.2,,.2,.01]);
22
- export const sound_destroyObject =new LJS.Sound([.5,,1e3,.02,,.2,1,3,.1,,,,,1,-30,.5,,.5]);
23
- export const sound_die = new LJS.Sound([.5,.4,126,.05,,.2,1,2.09,,-4,,,1,1,1,.4,.03]);
24
- export const sound_jump = new LJS.Sound([.4,.2,250,.04,,.04,,,1,,,,,3]);
25
- export const sound_dodge = new LJS.Sound([.4,.2,150,.05,,.05,,,-1,,,,,4,,,,,.02]);
26
- export const sound_walk = new LJS.Sound([.3,.1,50,.005,,.01,4,,,,,,,,10,,,.5]);
27
- export const sound_explosion = new LJS.Sound([2,.2,72,.01,.01,.2,4,,,,,,,1,,.5,.1,.5,.02]);
28
- export const sound_grenade = new LJS.Sound([.5,.01,300,,,.02,3,.22,,,-9,.2,,,,,,.5]);
29
- export const sound_score = new LJS.Sound([,,783,,.03,.02,1,2,,,940,.03,,,,,.2,.6,,.06]);
30
-
31
- ///////////////////////////////////////////////////////////////////////////////
32
- // special effects
33
-
34
- export const persistentParticleDestroyCallback = (particle)=>
35
- {
36
- // draw particle to tile layer on death
37
- LJS.ASSERT(!particle.tileInfo, 'quick draw to tile layer uses canvas 2d so must be untextured');
38
- if (particle.groundObject)
39
- {
40
- GameLevel.foregroundTileLayer.drawTile(particle.pos, particle.size, particle.tileInfo, particle.color, particle.angle, particle.mirror);
41
- }
42
- }
43
-
44
- export function makeBlood(pos, amount) { makeDebris(pos, hsl(0,1,.5), amount, .1, 0); }
45
- export function makeDebris(pos, color = hsl(), amount = 50, size=.2, restitution = .3)
46
- {
47
- const color2 = color.lerp(hsl(), .5);
48
- const emitter = new LJS.ParticleEmitter(
49
- pos, 0, .5, .1, amount/.1, 3.14, // pos, angle, size, time, rate, cone
50
- 0, // tileInfo
51
- color, color2, // colorStartA, colorStartB
52
- color, color2, // colorEndA, colorEndB
53
- 3, size,size, .1, .05, // time, sizeStart, sizeEnd, speed, angleSpeed
54
- 1, .95, .4, 3.14, 0, // damp, angleDamp, gravity, particleCone, fade
55
- .5, 1 // randomness, collide
56
- );
57
- emitter.restitution = restitution;
58
- emitter.particleDestroyCallback = persistentParticleDestroyCallback;
59
- return emitter;
60
- }
61
-
62
- ///////////////////////////////////////////////////////////////////////////////
63
-
64
- export function explosion(pos, radius=3)
65
- {
66
- LJS.ASSERT(radius > 0);
67
-
68
- sound_explosion.play(pos);
69
-
70
- {
71
- // destroy level
72
- const layer = GameLevel.tileLayers[1];
73
- layer.redrawStart();
74
- for (let x = -radius; x < radius; ++x)
75
- {
76
- const h = (radius*radius - x*x)**.5;
77
- for (let y = -h; y <= h; ++y)
78
- destroyTile(pos.add(vec2(x,y)), 0, 0);
79
- }
80
- // cleanup neighbors
81
- const cleanupRadius = radius + 2;
82
- for (let x = -cleanupRadius; x < cleanupRadius; ++x)
83
- {
84
- const h = (cleanupRadius**2 - x**2)**.5;
85
- for (let y = -h; y < h; ++y)
86
- GameLevel.decorateTile(pos.add(vec2(x,y)).floor(), layer);
87
- }
88
- layer.redrawEnd();
89
- }
90
-
91
- // kill/push objects
92
- LJS.engineObjectsCallback(pos, radius*3, (o)=>
93
- {
94
- const damage = radius*2;
95
- const d = o.pos.distance(pos);
96
- if (o.isGameObject)
97
- {
98
- // do damage
99
- d < radius && o.damage(damage);
100
- }
101
-
102
- // push
103
- const p = LJS.percent(d, 2*radius, radius);
104
- const force = o.pos.subtract(pos).normalize(p*radius*.2);
105
- o.applyForce(force);
106
- });
107
-
108
- // smoke
109
- new LJS.ParticleEmitter(
110
- pos, 0, // pos, angle
111
- radius/2, .2, 50*radius, 3.14,// emitSize, emitTime, rate, cone
112
- 0, // tileInfo
113
- hsl(0,0,0), hsl(0,0,0), // colorStartA, colorStartB
114
- hsl(0,0,0,0), hsl(0,0,0,0), // colorEndA, colorEndB
115
- 1, .5, 2, .2, .05, // time, sizeStart, sizeEnd, speed, angleSpeed
116
- .9, 1, -.3, 3.14, .1, // damp, angleDamp, gravity, particleCone, fade
117
- .5, 0, 0, 0, 1e8 // randomness, collide, additive, colorLinear, renderOrder
118
- );
119
-
120
- // fire
121
- new LJS.ParticleEmitter(
122
- pos, 0, // pos, angle
123
- radius/2, .1, 100*radius, 3.14, // emitSize, emitTime, rate, cone
124
- 0, // tileInfo
125
- rgb(1,.5,.1), rgb(1,.1,.1), // colorStartA, colorStartB
126
- rgb(1,.5,.1,0), rgb(1,.1,.1,0), // colorEndA, colorEndB
127
- .7, .8, .2, .2, .05, // time, sizeStart, sizeEnd, speed, angleSpeed
128
- .9, 1, -.2, 3.14, .05, // damp, angleDamp, gravity, particleCone, fade
129
- .5, 0, 1, 0, 1e9 // randomness, collide, additive, colorLinear, renderOrder
130
- );
131
- }
132
-
133
- ///////////////////////////////////////////////////////////////////////////////
134
-
135
- export function destroyTile(pos, makeSound = 1, cleanup = 1)
136
- {
137
- // pos must be an int
138
- pos = pos.floor();
139
-
140
- // destroy tile
141
- const layer = GameLevel.foregroundTileLayer;
142
- const tileType = layer.getCollisionData(pos);
143
- if (!tileType)
144
- return true;
145
-
146
- const centerPos = pos.add(vec2(.5));
147
- const layerData = layer.getData(pos);
148
- if (!layerData || tileType == GameLevel.tileType_solid)
149
- return false;
150
-
151
- // create effects
152
- makeDebris(centerPos, layerData.color.mutate());
153
- makeSound && sound_destroyObject.play(centerPos);
154
-
155
- // set and clear tile
156
- layer.clearData(pos, true);
157
- layer.setCollisionData(pos, GameLevel.tileType_empty);
158
-
159
- // cleanup neighbors and rebuild WebGL
160
- if (cleanup)
161
- {
162
- const layer = GameLevel.tileLayers[1];
163
- layer.redrawStart();
164
- for (let i=-1;i<=1;++i)
165
- for (let j=-1;j<=1;++j)
166
- GameLevel.decorateTile(pos.add(vec2(i,j)), layer);
167
- layer.redrawEnd();
168
- }
169
-
170
- return true;
171
- }
172
-
173
- ///////////////////////////////////////////////////////////////////////////////
174
- // sky with background gradient, stars, and planets
175
-
176
- export class Sky extends LJS.EngineObject
177
- {
178
- constructor()
179
- {
180
- super();
181
-
182
- this.renderOrder = -1e4;
183
- this.seed = LJS.randInt(1e9);
184
- this.skyColor = LJS.randColor(hsl(0,0,.5), hsl(0,0,.9));
185
- this.horizonColor = this.skyColor.subtract(hsl(0,0,.05,0)).mutate(.3);
186
- }
187
-
188
- render()
189
- {
190
- // fill background with a gradient
191
- const canvas = LJS.mainCanvas;
192
- LJS.drawRectGradient(LJS.cameraPos, LJS.getCameraSize(), this.skyColor, this.horizonColor);
193
-
194
- // draw stars
195
- LJS.setBlendMode(true);
196
- const random = new LJS.RandomGenerator(this.seed);
197
- for (let i = 1e3; i--;)
198
- {
199
- const size = random.float(.5,2)**2;
200
- const speed = random.float() < .9 ? random.float(5) : random.float(9,99);
201
- const color = hsl(random.float(-.3,.2), random.float(), random.float());
202
- const extraSpace = 50;
203
- const w = canvas.width+2*extraSpace, h = canvas.height+2*extraSpace;
204
- const screenPos = vec2(
205
- (random.float(w)+LJS.time*speed)%w-extraSpace,
206
- (random.float(h)+LJS.time*speed*random.float())%h-extraSpace);
207
- LJS.drawRect(screenPos, vec2(size), color, 0, undefined, true)
208
- }
209
- LJS.setBlendMode(false);
210
- }
211
- }
212
-
213
- ///////////////////////////////////////////////////////////////////////////////
214
- // parallax background mountain ranges
215
-
216
- export class ParallaxLayer extends LJS.CanvasLayer
217
- {
218
- constructor(pos, topColor, bottomColor, depth)
219
- {
220
- const renderOrder = depth - 3e3;
221
- const canvasSize = vec2(512, 256);
222
- super(pos, vec2(), 0, renderOrder, canvasSize);
223
- this.centerPos = pos;
224
- this.depth = depth;
225
-
226
- // create a gradient for the mountains
227
- const w = canvasSize.x, h = canvasSize.y;
228
- for (let i = h; i--;)
229
- {
230
- // draw a 1 pixel gradient line on the left side of the canvas
231
- const p = i/h;
232
- this.context.fillStyle = topColor.lerp(bottomColor, p);
233
- this.context.fillRect(0, i, 1, 1);
234
- }
235
-
236
- // draw random mountains
237
- const pointiness = .2; // how pointy the mountains are
238
- const levelness = .005; // how much the mountains level out
239
- const slopeRange = 1; // max slope of the mountains
240
- const startGroundLevel = h/2;
241
- let y = startGroundLevel, groundSlope = LJS.rand(-slopeRange, slopeRange);
242
- for (let x=w; x--;)
243
- {
244
- // pull slope towards start ground level
245
- y += groundSlope -= (y-startGroundLevel)*levelness;
246
-
247
- // randomly change slope
248
- if (LJS.rand() < pointiness)
249
- groundSlope = LJS.rand(-slopeRange, slopeRange);
250
-
251
- // draw 1 pixel wide vertical slice of mountain
252
- this.context.drawImage(this.canvas, 0, 0, 1, h, x, y, 1, h - y);
253
- }
254
-
255
- // remove gradient sliver from left side
256
- this.context.clearRect(0,0,1,h);
257
-
258
- // make WebGL texture
259
- this.updateWebGL();
260
- }
261
-
262
- render()
263
- {
264
- const canvasSize = vec2(this.canvas.width, this.canvas.height);
265
- const depth = this.depth
266
- const distance = 4 + depth;
267
- const parallax = vec2(150, 30).scale(depth**2+1);
268
- const levelCenter = this.centerPos;
269
- const cameraDeltaFromCenter = LJS.cameraPos.subtract(levelCenter)
270
- .divide(levelCenter.scale(-1).divide(parallax));
271
- const scale = distance/LJS.cameraScale;
272
- const positionOffset = vec2(0, 2-depth);
273
- const cameraOffset = cameraDeltaFromCenter.scale(1/LJS.cameraScale);
274
- this.pos = LJS.cameraPos.add(positionOffset).add(cameraOffset);
275
- this.size = canvasSize.scale(scale);
276
- super.render();
277
- }
278
- }