Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
FogNetwork
GitHub Repository: FogNetwork/Tsunami
Path: blob/main/public/games/files/asteroids/game.js
1036 views
1
// Canvas Asteroids
2
//
3
// Copyright (c) 2010 Doug McInnes
4
//
5
6
KEY_CODES = {
7
32: 'space',
8
37: 'left',
9
38: 'up',
10
39: 'right',
11
40: 'down',
12
70: 'f',
13
71: 'g',
14
72: 'h',
15
77: 'm',
16
80: 'p'
17
}
18
19
KEY_STATUS = { keyDown:false };
20
for (code in KEY_CODES) {
21
KEY_STATUS[KEY_CODES[code]] = false;
22
}
23
24
$(window).keydown(function (e) {
25
KEY_STATUS.keyDown = true;
26
if (KEY_CODES[e.keyCode]) {
27
e.preventDefault();
28
KEY_STATUS[KEY_CODES[e.keyCode]] = true;
29
}
30
}).keyup(function (e) {
31
KEY_STATUS.keyDown = false;
32
if (KEY_CODES[e.keyCode]) {
33
e.preventDefault();
34
KEY_STATUS[KEY_CODES[e.keyCode]] = false;
35
}
36
});
37
38
GRID_SIZE = 60;
39
40
Matrix = function (rows, columns) {
41
var i, j;
42
this.data = new Array(rows);
43
for (i = 0; i < rows; i++) {
44
this.data[i] = new Array(columns);
45
}
46
47
this.configure = function (rot, scale, transx, transy) {
48
var rad = (rot * Math.PI)/180;
49
var sin = Math.sin(rad) * scale;
50
var cos = Math.cos(rad) * scale;
51
this.set(cos, -sin, transx,
52
sin, cos, transy);
53
};
54
55
this.set = function () {
56
var k = 0;
57
for (i = 0; i < rows; i++) {
58
for (j = 0; j < columns; j++) {
59
this.data[i][j] = arguments[k];
60
k++;
61
}
62
}
63
}
64
65
this.multiply = function () {
66
var vector = new Array(rows);
67
for (i = 0; i < rows; i++) {
68
vector[i] = 0;
69
for (j = 0; j < columns; j++) {
70
vector[i] += this.data[i][j] * arguments[j];
71
}
72
}
73
return vector;
74
};
75
};
76
77
Sprite = function () {
78
this.init = function (name, points) {
79
this.name = name;
80
this.points = points;
81
82
this.vel = {
83
x: 0,
84
y: 0,
85
rot: 0
86
};
87
88
this.acc = {
89
x: 0,
90
y: 0,
91
rot: 0
92
};
93
};
94
95
this.children = {};
96
97
this.visible = false;
98
this.reap = false;
99
this.bridgesH = true;
100
this.bridgesV = true;
101
102
this.collidesWith = [];
103
104
this.x = 0;
105
this.y = 0;
106
this.rot = 0;
107
this.scale = 1;
108
109
this.currentNode = null;
110
this.nextSprite = null;
111
112
this.preMove = null;
113
this.postMove = null;
114
115
this.run = function(delta) {
116
117
this.move(delta);
118
this.updateGrid();
119
120
this.context.save();
121
this.configureTransform();
122
this.draw();
123
124
var canidates = this.findCollisionCanidates();
125
126
this.matrix.configure(this.rot, this.scale, this.x, this.y);
127
this.checkCollisionsAgainst(canidates);
128
129
this.context.restore();
130
131
if (this.bridgesH && this.currentNode && this.currentNode.dupe.horizontal) {
132
this.x += this.currentNode.dupe.horizontal;
133
this.context.save();
134
this.configureTransform();
135
this.draw();
136
this.checkCollisionsAgainst(canidates);
137
this.context.restore();
138
if (this.currentNode) {
139
this.x -= this.currentNode.dupe.horizontal;
140
}
141
}
142
if (this.bridgesV && this.currentNode && this.currentNode.dupe.vertical) {
143
this.y += this.currentNode.dupe.vertical;
144
this.context.save();
145
this.configureTransform();
146
this.draw();
147
this.checkCollisionsAgainst(canidates);
148
this.context.restore();
149
if (this.currentNode) {
150
this.y -= this.currentNode.dupe.vertical;
151
}
152
}
153
if (this.bridgesH && this.bridgesV &&
154
this.currentNode &&
155
this.currentNode.dupe.vertical &&
156
this.currentNode.dupe.horizontal) {
157
this.x += this.currentNode.dupe.horizontal;
158
this.y += this.currentNode.dupe.vertical;
159
this.context.save();
160
this.configureTransform();
161
this.draw();
162
this.checkCollisionsAgainst(canidates);
163
this.context.restore();
164
if (this.currentNode) {
165
this.x -= this.currentNode.dupe.horizontal;
166
this.y -= this.currentNode.dupe.vertical;
167
}
168
}
169
};
170
this.move = function (delta) {
171
if (!this.visible) return;
172
this.transPoints = null; // clear cached points
173
174
if ($.isFunction(this.preMove)) {
175
this.preMove(delta);
176
}
177
178
this.vel.x += this.acc.x * delta;
179
this.vel.y += this.acc.y * delta;
180
this.x += this.vel.x * delta;
181
this.y += this.vel.y * delta;
182
this.rot += this.vel.rot * delta;
183
if (this.rot > 360) {
184
this.rot -= 360;
185
} else if (this.rot < 0) {
186
this.rot += 360;
187
}
188
189
if ($.isFunction(this.postMove)) {
190
this.postMove(delta);
191
}
192
};
193
this.updateGrid = function () {
194
if (!this.visible) return;
195
var gridx = Math.floor(this.x / GRID_SIZE);
196
var gridy = Math.floor(this.y / GRID_SIZE);
197
gridx = (gridx >= this.grid.length) ? 0 : gridx;
198
gridy = (gridy >= this.grid[0].length) ? 0 : gridy;
199
gridx = (gridx < 0) ? this.grid.length-1 : gridx;
200
gridy = (gridy < 0) ? this.grid[0].length-1 : gridy;
201
var newNode = this.grid[gridx][gridy];
202
if (newNode != this.currentNode) {
203
if (this.currentNode) {
204
this.currentNode.leave(this);
205
}
206
newNode.enter(this);
207
this.currentNode = newNode;
208
}
209
210
if (KEY_STATUS.g && this.currentNode) {
211
this.context.lineWidth = 3.0;
212
this.context.strokeStyle = 'green';
213
this.context.strokeRect(gridx*GRID_SIZE+2, gridy*GRID_SIZE+2, GRID_SIZE-4, GRID_SIZE-4);
214
this.context.strokeStyle = 'black';
215
this.context.lineWidth = 1.0;
216
}
217
};
218
this.configureTransform = function () {
219
if (!this.visible) return;
220
221
var rad = (this.rot * Math.PI)/180;
222
223
this.context.translate(this.x, this.y);
224
this.context.rotate(rad);
225
this.context.scale(this.scale, this.scale);
226
};
227
this.draw = function () {
228
if (!this.visible) return;
229
230
this.context.lineWidth = 1.0 / this.scale;
231
232
for (child in this.children) {
233
this.children[child].draw();
234
}
235
236
this.context.beginPath();
237
238
this.context.moveTo(this.points[0], this.points[1]);
239
for (var i = 1; i < this.points.length/2; i++) {
240
var xi = i*2;
241
var yi = xi + 1;
242
this.context.lineTo(this.points[xi], this.points[yi]);
243
}
244
245
this.context.closePath();
246
this.context.stroke();
247
};
248
this.findCollisionCanidates = function () {
249
if (!this.visible || !this.currentNode) return [];
250
var cn = this.currentNode;
251
var canidates = [];
252
if (cn.nextSprite) canidates.push(cn.nextSprite);
253
if (cn.north.nextSprite) canidates.push(cn.north.nextSprite);
254
if (cn.south.nextSprite) canidates.push(cn.south.nextSprite);
255
if (cn.east.nextSprite) canidates.push(cn.east.nextSprite);
256
if (cn.west.nextSprite) canidates.push(cn.west.nextSprite);
257
if (cn.north.east.nextSprite) canidates.push(cn.north.east.nextSprite);
258
if (cn.north.west.nextSprite) canidates.push(cn.north.west.nextSprite);
259
if (cn.south.east.nextSprite) canidates.push(cn.south.east.nextSprite);
260
if (cn.south.west.nextSprite) canidates.push(cn.south.west.nextSprite);
261
return canidates
262
};
263
this.checkCollisionsAgainst = function (canidates) {
264
for (var i = 0; i < canidates.length; i++) {
265
var ref = canidates[i];
266
do {
267
this.checkCollision(ref);
268
ref = ref.nextSprite;
269
} while (ref)
270
}
271
};
272
this.checkCollision = function (other) {
273
if (!other.visible ||
274
this == other ||
275
this.collidesWith.indexOf(other.name) == -1) return;
276
var trans = other.transformedPoints();
277
var px, py;
278
var count = trans.length/2;
279
for (var i = 0; i < count; i++) {
280
px = trans[i*2];
281
py = trans[i*2 + 1];
282
// mozilla doesn't take into account transforms with isPointInPath >:-P
283
if (($.browser.mozilla) ? this.pointInPolygon(px, py) : this.context.isPointInPath(px, py)) {
284
other.collision(this);
285
this.collision(other);
286
return;
287
}
288
}
289
};
290
this.pointInPolygon = function (x, y) {
291
var points = this.transformedPoints();
292
var j = 2;
293
var y0, y1;
294
var oddNodes = false;
295
for (var i = 0; i < points.length; i += 2) {
296
y0 = points[i + 1];
297
y1 = points[j + 1];
298
if ((y0 < y && y1 >= y) ||
299
(y1 < y && y0 >= y)) {
300
if (points[i]+(y-y0)/(y1-y0)*(points[j]-points[i]) < x) {
301
oddNodes = !oddNodes;
302
}
303
}
304
j += 2
305
if (j == points.length) j = 0;
306
}
307
return oddNodes;
308
};
309
this.collision = function () {
310
};
311
this.die = function () {
312
this.visible = false;
313
this.reap = true;
314
if (this.currentNode) {
315
this.currentNode.leave(this);
316
this.currentNode = null;
317
}
318
};
319
this.transformedPoints = function () {
320
if (this.transPoints) return this.transPoints;
321
var trans = new Array(this.points.length);
322
this.matrix.configure(this.rot, this.scale, this.x, this.y);
323
for (var i = 0; i < this.points.length/2; i++) {
324
var xi = i*2;
325
var yi = xi + 1;
326
var pts = this.matrix.multiply(this.points[xi], this.points[yi], 1);
327
trans[xi] = pts[0];
328
trans[yi] = pts[1];
329
}
330
this.transPoints = trans; // cache translated points
331
return trans;
332
};
333
this.isClear = function () {
334
if (this.collidesWith.length == 0) return true;
335
var cn = this.currentNode;
336
if (cn == null) {
337
var gridx = Math.floor(this.x / GRID_SIZE);
338
var gridy = Math.floor(this.y / GRID_SIZE);
339
gridx = (gridx >= this.grid.length) ? 0 : gridx;
340
gridy = (gridy >= this.grid[0].length) ? 0 : gridy;
341
cn = this.grid[gridx][gridy];
342
}
343
return (cn.isEmpty(this.collidesWith) &&
344
cn.north.isEmpty(this.collidesWith) &&
345
cn.south.isEmpty(this.collidesWith) &&
346
cn.east.isEmpty(this.collidesWith) &&
347
cn.west.isEmpty(this.collidesWith) &&
348
cn.north.east.isEmpty(this.collidesWith) &&
349
cn.north.west.isEmpty(this.collidesWith) &&
350
cn.south.east.isEmpty(this.collidesWith) &&
351
cn.south.west.isEmpty(this.collidesWith));
352
};
353
this.wrapPostMove = function () {
354
if (this.x > Game.canvasWidth) {
355
this.x = 0;
356
} else if (this.x < 0) {
357
this.x = Game.canvasWidth;
358
}
359
if (this.y > Game.canvasHeight) {
360
this.y = 0;
361
} else if (this.y < 0) {
362
this.y = Game.canvasHeight;
363
}
364
};
365
366
};
367
368
Ship = function () {
369
this.init("ship",
370
[-5, 4,
371
0, -12,
372
5, 4]);
373
374
this.children.exhaust = new Sprite();
375
this.children.exhaust.init("exhaust",
376
[-3, 6,
377
0, 11,
378
3, 6]);
379
380
this.bulletCounter = 0;
381
382
this.postMove = this.wrapPostMove;
383
384
this.collidesWith = ["asteroid", "bigalien", "alienbullet"];
385
386
this.preMove = function (delta) {
387
if (KEY_STATUS.left) {
388
this.vel.rot = -6;
389
} else if (KEY_STATUS.right) {
390
this.vel.rot = 6;
391
} else {
392
this.vel.rot = 0;
393
}
394
395
if (KEY_STATUS.up) {
396
var rad = ((this.rot-90) * Math.PI)/180;
397
this.acc.x = 0.5 * Math.cos(rad);
398
this.acc.y = 0.5 * Math.sin(rad);
399
this.children.exhaust.visible = Math.random() > 0.1;
400
} else {
401
this.acc.x = 0;
402
this.acc.y = 0;
403
this.children.exhaust.visible = false;
404
}
405
406
if (this.bulletCounter > 0) {
407
this.bulletCounter -= delta;
408
}
409
if (KEY_STATUS.space) {
410
if (this.bulletCounter <= 0) {
411
this.bulletCounter = 10;
412
for (var i = 0; i < this.bullets.length; i++) {
413
if (!this.bullets[i].visible) {
414
SFX.laser();
415
var bullet = this.bullets[i];
416
var rad = ((this.rot-90) * Math.PI)/180;
417
var vectorx = Math.cos(rad);
418
var vectory = Math.sin(rad);
419
// move to the nose of the ship
420
bullet.x = this.x + vectorx * 4;
421
bullet.y = this.y + vectory * 4;
422
bullet.vel.x = 6 * vectorx + this.vel.x;
423
bullet.vel.y = 6 * vectory + this.vel.y;
424
bullet.visible = true;
425
break;
426
}
427
}
428
}
429
}
430
431
// limit the ship's speed
432
if (Math.sqrt(this.vel.x * this.vel.x + this.vel.y * this.vel.y) > 8) {
433
this.vel.x *= 0.95;
434
this.vel.y *= 0.95;
435
}
436
};
437
438
this.collision = function (other) {
439
SFX.explosion();
440
Game.explosionAt(other.x, other.y);
441
Game.FSM.state = 'player_died';
442
this.visible = false;
443
this.currentNode.leave(this);
444
this.currentNode = null;
445
Game.lives--;
446
};
447
448
};
449
Ship.prototype = new Sprite();
450
451
BigAlien = function () {
452
this.init("bigalien",
453
[-20, 0,
454
-12, -4,
455
12, -4,
456
20, 0,
457
12, 4,
458
-12, 4,
459
-20, 0,
460
20, 0]);
461
462
this.children.top = new Sprite();
463
this.children.top.init("bigalien_top",
464
[-8, -4,
465
-6, -6,
466
6, -6,
467
8, -4]);
468
this.children.top.visible = true;
469
470
this.children.bottom = new Sprite();
471
this.children.bottom.init("bigalien_top",
472
[ 8, 4,
473
6, 6,
474
-6, 6,
475
-8, 4]);
476
this.children.bottom.visible = true;
477
478
this.collidesWith = ["asteroid", "ship", "bullet"];
479
480
this.bridgesH = false;
481
482
this.bullets = [];
483
this.bulletCounter = 0;
484
485
this.newPosition = function () {
486
if (Math.random() < 0.5) {
487
this.x = -20;
488
this.vel.x = 1.5;
489
} else {
490
this.x = Game.canvasWidth + 20;
491
this.vel.x = -1.5;
492
}
493
this.y = Math.random() * Game.canvasHeight;
494
};
495
496
this.setup = function () {
497
this.newPosition();
498
499
for (var i = 0; i < 3; i++) {
500
var bull = new AlienBullet();
501
this.bullets.push(bull);
502
Game.sprites.push(bull);
503
}
504
};
505
506
this.preMove = function (delta) {
507
var cn = this.currentNode;
508
if (cn == null) return;
509
510
var topCount = 0;
511
if (cn.north.nextSprite) topCount++;
512
if (cn.north.east.nextSprite) topCount++;
513
if (cn.north.west.nextSprite) topCount++;
514
515
var bottomCount = 0;
516
if (cn.south.nextSprite) bottomCount++;
517
if (cn.south.east.nextSprite) bottomCount++;
518
if (cn.south.west.nextSprite) bottomCount++;
519
520
if (topCount > bottomCount) {
521
this.vel.y = 1;
522
} else if (topCount < bottomCount) {
523
this.vel.y = -1;
524
} else if (Math.random() < 0.01) {
525
this.vel.y = -this.vel.y;
526
}
527
528
this.bulletCounter -= delta;
529
if (this.bulletCounter <= 0) {
530
this.bulletCounter = 22;
531
for (var i = 0; i < this.bullets.length; i++) {
532
if (!this.bullets[i].visible) {
533
bullet = this.bullets[i];
534
var rad = 2 * Math.PI * Math.random();
535
var vectorx = Math.cos(rad);
536
var vectory = Math.sin(rad);
537
bullet.x = this.x;
538
bullet.y = this.y;
539
bullet.vel.x = 6 * vectorx;
540
bullet.vel.y = 6 * vectory;
541
bullet.visible = true;
542
SFX.laser();
543
break;
544
}
545
}
546
}
547
548
};
549
550
BigAlien.prototype.collision = function (other) {
551
if (other.name == "bullet") Game.score += 200;
552
SFX.explosion();
553
Game.explosionAt(other.x, other.y);
554
this.visible = false;
555
this.newPosition();
556
};
557
558
this.postMove = function () {
559
if (this.y > Game.canvasHeight) {
560
this.y = 0;
561
} else if (this.y < 0) {
562
this.y = Game.canvasHeight;
563
}
564
565
if ((this.vel.x > 0 && this.x > Game.canvasWidth + 20) ||
566
(this.vel.x < 0 && this.x < -20)) {
567
// why did the alien cross the road?
568
this.visible = false;
569
this.newPosition();
570
}
571
}
572
};
573
BigAlien.prototype = new Sprite();
574
575
Bullet = function () {
576
this.init("bullet", [0, 0]);
577
this.time = 0;
578
this.bridgesH = false;
579
this.bridgesV = false;
580
this.postMove = this.wrapPostMove;
581
// asteroid can look for bullets so doesn't have
582
// to be other way around
583
//this.collidesWith = ["asteroid"];
584
585
this.configureTransform = function () {};
586
this.draw = function () {
587
if (this.visible) {
588
this.context.save();
589
this.context.lineWidth = 2;
590
this.context.beginPath();
591
this.context.moveTo(this.x-1, this.y-1);
592
this.context.lineTo(this.x+1, this.y+1);
593
this.context.moveTo(this.x+1, this.y-1);
594
this.context.lineTo(this.x-1, this.y+1);
595
this.context.stroke();
596
this.context.restore();
597
}
598
};
599
this.preMove = function (delta) {
600
if (this.visible) {
601
this.time += delta;
602
}
603
if (this.time > 50) {
604
this.visible = false;
605
this.time = 0;
606
}
607
};
608
this.collision = function (other) {
609
this.time = 0;
610
this.visible = false;
611
this.currentNode.leave(this);
612
this.currentNode = null;
613
};
614
this.transformedPoints = function (other) {
615
return [this.x, this.y];
616
};
617
618
};
619
Bullet.prototype = new Sprite();
620
621
AlienBullet = function () {
622
this.init("alienbullet");
623
624
this.draw = function () {
625
if (this.visible) {
626
this.context.save();
627
this.context.lineWidth = 2;
628
this.context.beginPath();
629
this.context.moveTo(this.x, this.y);
630
this.context.lineTo(this.x-this.vel.x, this.y-this.vel.y);
631
this.context.stroke();
632
this.context.restore();
633
}
634
};
635
};
636
AlienBullet.prototype = new Bullet();
637
638
Asteroid = function () {
639
this.init("asteroid",
640
[-10, 0,
641
-5, 7,
642
-3, 4,
643
1, 10,
644
5, 4,
645
10, 0,
646
5, -6,
647
2, -10,
648
-4, -10,
649
-4, -5]);
650
651
this.visible = true;
652
this.scale = 6;
653
this.postMove = this.wrapPostMove;
654
655
this.collidesWith = ["ship", "bullet", "bigalien", "alienbullet"];
656
657
this.collision = function (other) {
658
SFX.explosion();
659
if (other.name == "bullet") Game.score += 120 / this.scale;
660
this.scale /= 3;
661
if (this.scale > 0.5) {
662
// break into fragments
663
for (var i = 0; i < 3; i++) {
664
var roid = $.extend(true, {}, this);
665
roid.vel.x = Math.random() * 6 - 3;
666
roid.vel.y = Math.random() * 6 - 3;
667
if (Math.random() > 0.5) {
668
roid.points.reverse();
669
}
670
roid.vel.rot = Math.random() * 2 - 1;
671
roid.move(roid.scale * 3); // give them a little push
672
Game.sprites.push(roid);
673
}
674
}
675
Game.explosionAt(other.x, other.y);
676
this.die();
677
};
678
};
679
Asteroid.prototype = new Sprite();
680
681
Explosion = function () {
682
this.init("explosion");
683
684
this.bridgesH = false;
685
this.bridgesV = false;
686
687
this.lines = [];
688
for (var i = 0; i < 5; i++) {
689
var rad = 2 * Math.PI * Math.random();
690
var x = Math.cos(rad);
691
var y = Math.sin(rad);
692
this.lines.push([x, y, x*2, y*2]);
693
}
694
695
this.draw = function () {
696
if (this.visible) {
697
this.context.save();
698
this.context.lineWidth = 1.0 / this.scale;
699
this.context.beginPath();
700
for (var i = 0; i < 5; i++) {
701
var line = this.lines[i];
702
this.context.moveTo(line[0], line[1]);
703
this.context.lineTo(line[2], line[3]);
704
}
705
this.context.stroke();
706
this.context.restore();
707
}
708
};
709
710
this.preMove = function (delta) {
711
if (this.visible) {
712
this.scale += delta;
713
}
714
if (this.scale > 8) {
715
this.die();
716
}
717
};
718
};
719
Explosion.prototype = new Sprite();
720
721
GridNode = function () {
722
this.north = null;
723
this.south = null;
724
this.east = null;
725
this.west = null;
726
727
this.nextSprite = null;
728
729
this.dupe = {
730
horizontal: null,
731
vertical: null
732
};
733
734
this.enter = function (sprite) {
735
sprite.nextSprite = this.nextSprite;
736
this.nextSprite = sprite;
737
};
738
739
this.leave = function (sprite) {
740
var ref = this;
741
while (ref && (ref.nextSprite != sprite)) {
742
ref = ref.nextSprite;
743
}
744
if (ref) {
745
ref.nextSprite = sprite.nextSprite;
746
sprite.nextSprite = null;
747
}
748
};
749
750
this.eachSprite = function(sprite, callback) {
751
var ref = this;
752
while (ref.nextSprite) {
753
ref = ref.nextSprite;
754
callback.call(sprite, ref);
755
}
756
};
757
758
this.isEmpty = function (collidables) {
759
var empty = true;
760
var ref = this;
761
while (ref.nextSprite) {
762
ref = ref.nextSprite;
763
empty = !ref.visible || collidables.indexOf(ref.name) == -1
764
if (!empty) break;
765
}
766
return empty;
767
};
768
};
769
770
// borrowed from typeface-0.14.js
771
// http://typeface.neocracy.org
772
Text = {
773
renderGlyph: function (ctx, face, char) {
774
775
var glyph = face.glyphs[char];
776
777
if (glyph.o) {
778
779
var outline;
780
if (glyph.cached_outline) {
781
outline = glyph.cached_outline;
782
} else {
783
outline = glyph.o.split(' ');
784
glyph.cached_outline = outline;
785
}
786
787
var outlineLength = outline.length;
788
for (var i = 0; i < outlineLength; ) {
789
790
var action = outline[i++];
791
792
switch(action) {
793
case 'm':
794
ctx.moveTo(outline[i++], outline[i++]);
795
break;
796
case 'l':
797
ctx.lineTo(outline[i++], outline[i++]);
798
break;
799
800
case 'q':
801
var cpx = outline[i++];
802
var cpy = outline[i++];
803
ctx.quadraticCurveTo(outline[i++], outline[i++], cpx, cpy);
804
break;
805
806
case 'b':
807
var x = outline[i++];
808
var y = outline[i++];
809
ctx.bezierCurveTo(outline[i++], outline[i++], outline[i++], outline[i++], x, y);
810
break;
811
}
812
}
813
}
814
if (glyph.ha) {
815
ctx.translate(glyph.ha, 0);
816
}
817
},
818
819
renderText: function(text, size, x, y) {
820
this.context.save();
821
822
this.context.translate(x, y);
823
824
var pixels = size * 72 / (this.face.resolution * 100);
825
this.context.scale(pixels, -1 * pixels);
826
this.context.beginPath();
827
var chars = text.split('');
828
var charsLength = chars.length;
829
for (var i = 0; i < charsLength; i++) {
830
this.renderGlyph(this.context, this.face, chars[i]);
831
}
832
this.context.fill();
833
834
this.context.restore();
835
},
836
837
context: null,
838
face: null
839
};
840
841
SFX = {
842
laser: new Audio('39459__THE_bizniss__laser.wav'),
843
explosion: new Audio('51467__smcameron__missile_explosion.wav')
844
};
845
846
// preload audio
847
for (var sfx in SFX) {
848
(function () {
849
var audio = SFX[sfx];
850
audio.muted = true;
851
audio.play();
852
853
SFX[sfx] = function () {
854
if (!this.muted) {
855
if (audio.duration == 0) {
856
// somehow dropped out
857
audio.load();
858
audio.play();
859
} else {
860
audio.muted = false;
861
audio.currentTime = 0;
862
}
863
}
864
return audio;
865
}
866
})();
867
}
868
// pre-mute audio
869
SFX.muted = true;
870
871
Game = {
872
score: 0,
873
totalAsteroids: 5,
874
lives: 0,
875
876
canvasWidth: 800,
877
canvasHeight: 600,
878
879
sprites: [],
880
ship: null,
881
bigAlien: null,
882
883
nextBigAlienTime: null,
884
885
886
spawnAsteroids: function (count) {
887
if (!count) count = this.totalAsteroids;
888
for (var i = 0; i < count; i++) {
889
var roid = new Asteroid();
890
roid.x = Math.random() * this.canvasWidth;
891
roid.y = Math.random() * this.canvasHeight;
892
while (!roid.isClear()) {
893
roid.x = Math.random() * this.canvasWidth;
894
roid.y = Math.random() * this.canvasHeight;
895
}
896
roid.vel.x = Math.random() * 4 - 2;
897
roid.vel.y = Math.random() * 4 - 2;
898
if (Math.random() > 0.5) {
899
roid.points.reverse();
900
}
901
roid.vel.rot = Math.random() * 2 - 1;
902
Game.sprites.push(roid);
903
}
904
},
905
906
explosionAt: function (x, y) {
907
var splosion = new Explosion();
908
splosion.x = x;
909
splosion.y = y;
910
splosion.visible = true;
911
Game.sprites.push(splosion);
912
},
913
914
FSM: {
915
boot: function () {
916
Game.spawnAsteroids(5);
917
this.state = 'waiting';
918
},
919
waiting: function () {
920
Text.renderText(window.ipad ? 'Touch Screen to Start' : 'Press Space to Start', 36, Game.canvasWidth/2 - 270, Game.canvasHeight/2);
921
if (KEY_STATUS.space || window.gameStart) {
922
KEY_STATUS.space = false; // hack so we don't shoot right away
923
window.gameStart = false;
924
this.state = 'start';
925
}
926
},
927
start: function () {
928
for (var i = 0; i < Game.sprites.length; i++) {
929
if (Game.sprites[i].name == 'asteroid') {
930
Game.sprites[i].die();
931
} else if (Game.sprites[i].name == 'bullet' ||
932
Game.sprites[i].name == 'bigalien') {
933
Game.sprites[i].visible = false;
934
}
935
}
936
937
Game.score = 0;
938
Game.lives = 2;
939
Game.totalAsteroids = 2;
940
Game.spawnAsteroids();
941
942
Game.nextBigAlienTime = Date.now() + 30000 + (30000 * Math.random());
943
944
this.state = 'spawn_ship';
945
},
946
spawn_ship: function () {
947
Game.ship.x = Game.canvasWidth / 2;
948
Game.ship.y = Game.canvasHeight / 2;
949
if (Game.ship.isClear()) {
950
Game.ship.rot = 0;
951
Game.ship.vel.x = 0;
952
Game.ship.vel.y = 0;
953
Game.ship.visible = true;
954
this.state = 'run';
955
}
956
},
957
run: function () {
958
for (var i = 0; i < Game.sprites.length; i++) {
959
if (Game.sprites[i].name == 'asteroid') {
960
break;
961
}
962
}
963
if (i == Game.sprites.length) {
964
this.state = 'new_level';
965
}
966
if (!Game.bigAlien.visible &&
967
Date.now() > Game.nextBigAlienTime) {
968
Game.bigAlien.visible = true;
969
Game.nextBigAlienTime = Date.now() + (30000 * Math.random());
970
}
971
},
972
new_level: function () {
973
if (this.timer == null) {
974
this.timer = Date.now();
975
}
976
// wait a second before spawning more asteroids
977
if (Date.now() - this.timer > 1000) {
978
this.timer = null;
979
Game.totalAsteroids++;
980
if (Game.totalAsteroids > 12) Game.totalAsteroids = 12;
981
Game.spawnAsteroids();
982
this.state = 'run';
983
}
984
},
985
player_died: function () {
986
if (Game.lives < 0) {
987
this.state = 'end_game';
988
} else {
989
if (this.timer == null) {
990
this.timer = Date.now();
991
}
992
// wait a second before spawning
993
if (Date.now() - this.timer > 1000) {
994
this.timer = null;
995
this.state = 'spawn_ship';
996
}
997
}
998
},
999
end_game: function () {
1000
Text.renderText('GAME OVER', 50, Game.canvasWidth/2 - 160, Game.canvasHeight/2 + 10);
1001
if (this.timer == null) {
1002
this.timer = Date.now();
1003
}
1004
// wait 5 seconds then go back to waiting state
1005
if (Date.now() - this.timer > 5000) {
1006
this.timer = null;
1007
this.state = 'waiting';
1008
}
1009
1010
window.gameStart = false;
1011
},
1012
1013
execute: function () {
1014
this[this.state]();
1015
},
1016
state: 'boot'
1017
}
1018
1019
};
1020
1021
1022
$(function () {
1023
var canvas = $("#canvas");
1024
Game.canvasWidth = canvas.width();
1025
Game.canvasHeight = canvas.height();
1026
1027
var context = canvas[0].getContext("2d");
1028
1029
Text.context = context;
1030
Text.face = vector_battle;
1031
1032
var gridWidth = Math.round(Game.canvasWidth / GRID_SIZE);
1033
var gridHeight = Math.round(Game.canvasHeight / GRID_SIZE);
1034
var grid = new Array(gridWidth);
1035
for (var i = 0; i < gridWidth; i++) {
1036
grid[i] = new Array(gridHeight);
1037
for (var j = 0; j < gridHeight; j++) {
1038
grid[i][j] = new GridNode();
1039
}
1040
}
1041
1042
// set up the positional references
1043
for (var i = 0; i < gridWidth; i++) {
1044
for (var j = 0; j < gridHeight; j++) {
1045
var node = grid[i][j];
1046
node.north = grid[i][(j == 0) ? gridHeight-1 : j-1];
1047
node.south = grid[i][(j == gridHeight-1) ? 0 : j+1];
1048
node.west = grid[(i == 0) ? gridWidth-1 : i-1][j];
1049
node.east = grid[(i == gridWidth-1) ? 0 : i+1][j];
1050
}
1051
}
1052
1053
// set up borders
1054
for (var i = 0; i < gridWidth; i++) {
1055
grid[i][0].dupe.vertical = Game.canvasHeight;
1056
grid[i][gridHeight-1].dupe.vertical = -Game.canvasHeight;
1057
}
1058
1059
for (var j = 0; j < gridHeight; j++) {
1060
grid[0][j].dupe.horizontal = Game.canvasWidth;
1061
grid[gridWidth-1][j].dupe.horizontal = -Game.canvasWidth;
1062
}
1063
1064
var sprites = [];
1065
Game.sprites = sprites;
1066
1067
// so all the sprites can use it
1068
Sprite.prototype.context = context;
1069
Sprite.prototype.grid = grid;
1070
Sprite.prototype.matrix = new Matrix(2, 3);
1071
1072
var ship = new Ship();
1073
1074
ship.x = Game.canvasWidth / 2;
1075
ship.y = Game.canvasHeight / 2;
1076
1077
sprites.push(ship);
1078
1079
ship.bullets = [];
1080
for (var i = 0; i < 10; i++) {
1081
var bull = new Bullet();
1082
ship.bullets.push(bull);
1083
sprites.push(bull);
1084
}
1085
Game.ship = ship;
1086
1087
var bigAlien = new BigAlien();
1088
bigAlien.setup();
1089
sprites.push(bigAlien);
1090
Game.bigAlien = bigAlien;
1091
1092
var extraDude = new Ship();
1093
extraDude.scale = 0.6;
1094
extraDude.visible = true;
1095
extraDude.preMove = null;
1096
extraDude.children = [];
1097
1098
var i, j = 0;
1099
1100
var paused = false;
1101
var showFramerate = false;
1102
var avgFramerate = 0;
1103
var frameCount = 0;
1104
var elapsedCounter = 0;
1105
1106
var lastFrame = Date.now();
1107
var thisFrame;
1108
var elapsed;
1109
var delta;
1110
1111
var canvasNode = canvas[0];
1112
1113
// shim layer with setTimeout fallback
1114
// from here:
1115
// http://paulirish.com/2011/requestanimationframe-for-smart-animating/
1116
window.requestAnimFrame = (function () {
1117
return window.requestAnimationFrame ||
1118
window.webkitRequestAnimationFrame ||
1119
window.mozRequestAnimationFrame ||
1120
window.oRequestAnimationFrame ||
1121
window.msRequestAnimationFrame ||
1122
function (/* function */ callback, /* DOMElement */ element) {
1123
window.setTimeout(callback, 1000 / 60);
1124
};
1125
})();
1126
1127
var mainLoop = function () {
1128
context.clearRect(0, 0, Game.canvasWidth, Game.canvasHeight);
1129
1130
Game.FSM.execute();
1131
1132
if (KEY_STATUS.g) {
1133
context.beginPath();
1134
for (var i = 0; i < gridWidth; i++) {
1135
context.moveTo(i * GRID_SIZE, 0);
1136
context.lineTo(i * GRID_SIZE, Game.canvasHeight);
1137
}
1138
for (var j = 0; j < gridHeight; j++) {
1139
context.moveTo(0, j * GRID_SIZE);
1140
context.lineTo(Game.canvasWidth, j * GRID_SIZE);
1141
}
1142
context.closePath();
1143
context.stroke();
1144
}
1145
1146
thisFrame = Date.now();
1147
elapsed = thisFrame - lastFrame;
1148
lastFrame = thisFrame;
1149
delta = elapsed / 30;
1150
1151
for (i = 0; i < sprites.length; i++) {
1152
1153
sprites[i].run(delta);
1154
1155
if (sprites[i].reap) {
1156
sprites[i].reap = false;
1157
sprites.splice(i, 1);
1158
i--;
1159
}
1160
}
1161
1162
// score
1163
var score_text = ''+Game.score;
1164
Text.renderText(score_text, 18, Game.canvasWidth - 14 * score_text.length, 20);
1165
1166
// extra dudes
1167
for (i = 0; i < Game.lives; i++) {
1168
context.save();
1169
extraDude.x = Game.canvasWidth - (8 * (i + 1));
1170
extraDude.y = 32;
1171
extraDude.configureTransform();
1172
extraDude.draw();
1173
context.restore();
1174
}
1175
1176
if (showFramerate) {
1177
Text.renderText(''+avgFramerate, 24, Game.canvasWidth - 38, Game.canvasHeight - 2);
1178
}
1179
1180
frameCount++;
1181
elapsedCounter += elapsed;
1182
if (elapsedCounter > 1000) {
1183
elapsedCounter -= 1000;
1184
avgFramerate = frameCount;
1185
frameCount = 0;
1186
}
1187
1188
if (paused) {
1189
Text.renderText('PAUSED', 72, Game.canvasWidth/2 - 160, 120);
1190
} else {
1191
requestAnimFrame(mainLoop, canvasNode);
1192
}
1193
};
1194
1195
mainLoop();
1196
1197
$(window).keydown(function (e) {
1198
switch (KEY_CODES[e.keyCode]) {
1199
case 'f': // show framerate
1200
showFramerate = !showFramerate;
1201
break;
1202
case 'p': // pause
1203
paused = !paused;
1204
if (!paused) {
1205
// start up again
1206
lastFrame = Date.now();
1207
mainLoop();
1208
}
1209
break;
1210
case 'm': // mute
1211
SFX.muted = !SFX.muted;
1212
break;
1213
}
1214
});
1215
});
1216
1217
// vim: fdl=0
1218
1219