├── .DS_Store
├── README.md
├── crystalQuest.css
├── favicon.ico
├── img
├── .DS_Store
├── alien_bullet1.png
├── alien_bullet2.png
├── alien_bullet3.png
├── basic_alien.png
├── basic_alien_sp.png
├── big_crystal.png
├── blob_alien.png
├── bomb.png
├── bullet_alien.png
├── computer_alien.png
├── computer_hash.png
├── crystal.png
├── cs_alien.png
├── four_leg_alien.png
├── laser_alien.png
├── life.png
├── rm_basic_alien.png
├── rm_blob_alien.png
├── rm_bullet_alien.png
├── rm_computer_alien.png
├── rm_cs_alien.png
├── rm_four_leg_alien.png
├── rm_shooter_alien.png
├── rm_x_alien.png
├── shooter_alien.png
└── x_alien.png
├── index.html
├── jquery-2.1.1.js
├── keymaster.js
├── lib
├── .DS_Store
├── CSAlien.js
├── XSAlien.js
├── alienBullet.js
├── asteroid.js
├── basicAlien.js
├── bigCrystal.js
├── blobAlien.js
├── bomb.js
├── bullet.js
├── bulletAlien.js
├── clock.js
├── computerAlien.js
├── computerHash.js
├── crystal.js
├── crystalPoints.js
├── fourLegAlien.js
├── game.js
├── gate.js
├── laserAlien.js
├── movingObject.js
├── points.js
├── portal.js
├── ship.js
├── shooterAlien.js
├── utils.js
└── wave.js
└── sounds
├── .DS_Store
├── crystal.wav
├── laser.wav
├── levelSuccess.wav
├── reverbLaser.wav
├── shortLaser.wav
├── success.mp3
└── tristeza.wav
/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/haleymt/CrystalQuest/8bd29df2ca65d264fecf37aabd32659ba0a3c153/.DS_Store
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # CrystalQuest
2 | This is a remake of the classic game designed for the 1987 Macintosh. It uses HTML5 Canvas, Sprites, Javascript, and jQuery.
3 |
4 | Crystal Quest features **fifteen** different levels and **eight** different kinds of aliens (so far!). Each alien has its own look, its own dangers, and its own way of moving. Each wave increases in difficulty, and features increasingly perilous foes.
5 |
6 | See if you can beat it!
7 |
8 | [Play Crystal Quest](https://haleymt.github.io/CrystalQuest)
9 |
10 | ### Gameplay
11 | * Move your ship around using the direction keys
12 | * Scoop up all the crystals in order to open the gate.
13 | * Don't run into the aliens, asteroids, or portals or you'll lose a life.
14 | * Lose all your lives and it's game over.
15 | * You can use your cherry bombs to clear the screen if you're in danger.
16 | * If you collect the big crystal you'll gain a life and some bonus points.
17 | * Press the space bar to shoot some aliens.
18 |
19 | ### Aliens
20 |
21 | 
22 | 
23 | 
24 | 
25 | 
26 | 
27 | 
28 |
29 | Each alien changes direction regularly. Accomplishing this requires two parts. First, I have to create a random movement vector for the alien:
30 | ```javascript
31 | Util.randomVec = function (length) {
32 | var deg = 2 * Math.PI * Math.random();
33 | return Util.scale([Math.sin(deg), Math.cos(deg)], length);
34 | };
35 |
36 | Util.scale = function (vec, m) {
37 | return [vec[0] * m, vec[1] * m];
38 | };
39 | ```
40 | Next, I start an interval running when I create the alien that changes its movement vector every half second:
41 |
42 | ```javascript
43 | var that = this;
44 | this.dirInterval = setInterval( function () {
45 | that.vel = window.CrystalQuest.Util.randomVec(3);
46 | }, 500);
47 | ```
48 | If I want to change how fast an alien moves, I just change the argument for `randomVec`. A similar principle applies for creating and moving bullets.
49 |
50 | If an alien hits a wall I want it to bounce off in an opposite but random direction:
51 | ```javascript
52 | Wave.prototype.bounce = function (object) {
53 | xVel = object.vel[0];
54 | yVel = object.vel[1];
55 | var dirs = [-1, 1];
56 | var idx = Math.floor(Math.random() * 2);
57 |
58 | if (xVel < 0 && yVel < 0) {
59 | object.vel = [-xVel, dirs[idx] * yVel]
60 | } else if (xVel < 0 && yVel > 0) {
61 | object.vel = [dirs[idx] * xVel, -yVel]
62 | } else if (xVel > 0 && yVel > 0) {
63 | object.vel = [-xVel, dirs[idx] * yVel]
64 | } else if (xVel > 0 && yVel < 0) {
65 | object.vel = [dirs[idx] * xVel, -yVel]
66 | } else if (xVel === 0 && yVel !== 0) {
67 | object.vel = [xVel, -yVel]
68 | } else if (yVel === 0 && xVel !== 0) {
69 | object.vel = [-xVel, yVel]
70 | }
71 | };
72 | ```
73 | ### Waves
74 | Each wave's attributes are defined in an Object:
75 |
76 | ```javascript
77 | Game.WAVE_EIGHT = {
78 | numAsteroids: 10,
79 | numBombs: 1,
80 | numCrystals: 25,
81 | numComputerAliens: 5,
82 | numBasicAliens: 1,
83 | numBigCrystals: 0,
84 | numPoints: 3
85 | };
86 | ```
87 | The array `Game.WAVES` holds all of these objects. The game keeps track of what wave it's on by incrementing a counter that refers to an index in `Game.WAVES`.
88 |
89 | ### Intervals
90 | Javascript's `setInterval` is notoriously tricky. It is strict about scope, it doesn't return any values, and any child `setInterval`s nested inside of another parent `setInterval` will *keep running* even after a parent interval is cleared. This poses potentially huge performance problems. To fix it, I carefully clear any intervals I create throughout the game:
91 |
92 | * All intervals are defined at the top level (`this.interval` instead of `var interval`). Which means I can define a method on the top level that clears those intervals:
93 | ```javascript
94 | Game.prototype.stop = function() {
95 | if (this.interval) {
96 | clearInterval(this.interval);
97 | this.interval = null;
98 | }
99 | };
100 | ```
101 | I can then call `this.stop()` during the winning or losing scenario.
102 | * Upon destroying an alien, all of its intervals get cleared as well:
103 | ```javascript
104 | if (object instanceof CrystalQuest.ShooterAlien) {
105 | clearInterval(object.shootInterval);
106 | object.shootInterval = null;
107 | this.aliens.splice(this.aliens.indexOf(object), 1);
108 | ```
109 | * Upon finishing a level, I iterate through all the remaining aliens, and clear their movement and shooting intervals as well.
110 |
111 | ### High Scores
112 | For now, high scores are stored in the localStorage and not in a database.
113 |
114 | If you're running the game for the first time, it will create an item called `"high-scores"` upon initialization and set it to an empty array:
115 | ```javascript
116 | Game.prototype.run = function () {
117 | if (this.scores === null) {
118 | localStorage.setItem("high-scores", JSON.stringify([]));
119 | this.scores = JSON.parse(localStorage.getItem("high-scores"));
120 | }
121 | ...
122 | };
123 | ```
124 | The array will eventually contain a collection of Objects containing a name and score, which we'll set at the end of a wave like so:
125 | ```javascript
126 | Game.prototype.lose = function() {
127 | ...
128 | var score = parseInt($('#score').text())
129 | if ((this.scores.length < 10) || (score > this.scores[this.scores.length - 1]['score'])) {
130 | var name = prompt("You got a new high score! Enter your initials: ");
131 | if ((name !== "") && (name !== null)) {
132 | if (this.scores.length === 10) {
133 | this.scores.pop();
134 | }
135 | this.scores.push({'name': name, 'score': score})
136 | this.scores.sort( function(a, b) {
137 | return b['score'] - a['score'];
138 | });
139 | localStorage.setItem("high-scores", JSON.stringify(this.scores));
140 | }
141 | this.showHighScores();
142 | }
143 | ...
144 | };
145 | ```
146 | And then at the beginning of a new game, we'll just grab all the scores:
147 | ```javascript
148 | var Game = window.CrystalQuest.Game = function (xDim, yDim, ctx) {
149 | ...
150 | this.scores = JSON.parse(localStorage.getItem("high-scores"));
151 | ...
152 | };
153 | ```
154 | Easy peasy.
155 |
--------------------------------------------------------------------------------
/crystalQuest.css:
--------------------------------------------------------------------------------
1 | body {
2 | background: black;
3 | font-family: sans-serif;
4 | }
5 |
6 | .big-container {
7 | margin-left:auto;
8 | margin-right:auto;
9 | width: 790px;
10 | height: 560px;
11 | }
12 |
13 | #game-canvas {
14 | border-bottom: 1px solid white;
15 | }
16 |
17 | #info {
18 | position: absolute;
19 | left: 0;
20 | right: 0;
21 | color: white;
22 | }
23 |
24 | #info strong {
25 | line-height: 23px;
26 | }
27 |
28 | .instructions {
29 | padding-left:50px;
30 | float: left;
31 | padding-top: 10px;
32 | padding-bottom: 15px;
33 | }
34 |
35 | .keys {
36 | padding-right:50px;
37 | float: right;
38 | padding-top: 10px;
39 | padding-bottom: 15px;
40 | }
41 |
42 | code {
43 | border: 1px solid white;
44 | border-radius: 3px;
45 | padding: 1px 3px;
46 | }
47 |
48 | #container {
49 | height: 550px;
50 | position: relative;
51 | width: 780px;
52 | background: black;
53 | border: 1px solid white;
54 | border-bottom: 0px;
55 | }
56 |
57 | #controls {
58 | position: absolute;
59 | top: 50% !important;
60 | left: 50%;
61 | -webkit-transform: translate(-50%, -50%);
62 | -ms-transform: translate(-50%, -50%);
63 | -o-transform: translate(-50%, -50%);
64 | transform: translate(-50%, -50%);
65 | color: white;
66 | padding: 0px;
67 | display: inline-block
68 | }
69 |
70 | #controls ul {
71 | list-style-type: none;
72 | padding: 0px;
73 | font-family: 'Audiowide', cursive;
74 | }
75 |
76 | #controls p {
77 | font-family: 'Audiowide', cursive;
78 | }
79 |
80 | #controls h1 {
81 | font-family: 'Audiowide', cursive;
82 | }
83 |
84 | #controls h2,
85 | #controls h3 {
86 | font-family: 'Audiowide', cursive;
87 | color: rgb(255, 204, 0);
88 | }
89 |
90 | .other-game {
91 | display: inline-block;
92 | width: 100%;
93 | margin-top: 20px;
94 | font-size: 14px;
95 | margin-bottom: 20px;
96 | }
97 |
98 | .other-game a {
99 | font-family: 'Bungee Shade', cursive;
100 | color: white;
101 | text-decoration: none;
102 | font-size: 16px;
103 | }
104 |
105 | .other-game a:hover {
106 | opacity: 0.95;
107 | }
108 |
109 | #controls button {
110 | font-family: 'Press Start 2P', cursive;
111 | padding: 10px;
112 | cursor: pointer;
113 | border-radius: 3px;
114 | border: none;
115 | outline: none !important;
116 | background: white;
117 | margin-bottom: 10px;
118 | margin-right: 5px;
119 | margin-left: 5px;
120 | }
121 |
122 | #controls button:hover {
123 | background: rgb(255, 204, 0);
124 | }
125 |
126 | #controls button:focus {
127 | background: rgb(255, 223, 96);
128 | }
129 |
130 | #status-bar {
131 | height: 50px;
132 | width: 100%;
133 | background: black;
134 | color: white;
135 | border-bottom: 1px solid white;
136 | font-size: 20px;
137 | font-weight: bold;
138 | }
139 |
140 | #status-bar ul {
141 | margin: 0;
142 | padding: 0px;
143 | list-style-type: none;
144 | width: 100%;
145 | display: flex;
146 | align-items: center;
147 | justify-content: center;
148 | }
149 |
150 | #status-bar ul li {
151 | display: inline;
152 | padding: 10px;
153 | }
154 |
--------------------------------------------------------------------------------
/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/haleymt/CrystalQuest/8bd29df2ca65d264fecf37aabd32659ba0a3c153/favicon.ico
--------------------------------------------------------------------------------
/img/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/haleymt/CrystalQuest/8bd29df2ca65d264fecf37aabd32659ba0a3c153/img/.DS_Store
--------------------------------------------------------------------------------
/img/alien_bullet1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/haleymt/CrystalQuest/8bd29df2ca65d264fecf37aabd32659ba0a3c153/img/alien_bullet1.png
--------------------------------------------------------------------------------
/img/alien_bullet2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/haleymt/CrystalQuest/8bd29df2ca65d264fecf37aabd32659ba0a3c153/img/alien_bullet2.png
--------------------------------------------------------------------------------
/img/alien_bullet3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/haleymt/CrystalQuest/8bd29df2ca65d264fecf37aabd32659ba0a3c153/img/alien_bullet3.png
--------------------------------------------------------------------------------
/img/basic_alien.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/haleymt/CrystalQuest/8bd29df2ca65d264fecf37aabd32659ba0a3c153/img/basic_alien.png
--------------------------------------------------------------------------------
/img/basic_alien_sp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/haleymt/CrystalQuest/8bd29df2ca65d264fecf37aabd32659ba0a3c153/img/basic_alien_sp.png
--------------------------------------------------------------------------------
/img/big_crystal.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/haleymt/CrystalQuest/8bd29df2ca65d264fecf37aabd32659ba0a3c153/img/big_crystal.png
--------------------------------------------------------------------------------
/img/blob_alien.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/haleymt/CrystalQuest/8bd29df2ca65d264fecf37aabd32659ba0a3c153/img/blob_alien.png
--------------------------------------------------------------------------------
/img/bomb.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/haleymt/CrystalQuest/8bd29df2ca65d264fecf37aabd32659ba0a3c153/img/bomb.png
--------------------------------------------------------------------------------
/img/bullet_alien.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/haleymt/CrystalQuest/8bd29df2ca65d264fecf37aabd32659ba0a3c153/img/bullet_alien.png
--------------------------------------------------------------------------------
/img/computer_alien.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/haleymt/CrystalQuest/8bd29df2ca65d264fecf37aabd32659ba0a3c153/img/computer_alien.png
--------------------------------------------------------------------------------
/img/computer_hash.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/haleymt/CrystalQuest/8bd29df2ca65d264fecf37aabd32659ba0a3c153/img/computer_hash.png
--------------------------------------------------------------------------------
/img/crystal.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/haleymt/CrystalQuest/8bd29df2ca65d264fecf37aabd32659ba0a3c153/img/crystal.png
--------------------------------------------------------------------------------
/img/cs_alien.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/haleymt/CrystalQuest/8bd29df2ca65d264fecf37aabd32659ba0a3c153/img/cs_alien.png
--------------------------------------------------------------------------------
/img/four_leg_alien.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/haleymt/CrystalQuest/8bd29df2ca65d264fecf37aabd32659ba0a3c153/img/four_leg_alien.png
--------------------------------------------------------------------------------
/img/laser_alien.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/haleymt/CrystalQuest/8bd29df2ca65d264fecf37aabd32659ba0a3c153/img/laser_alien.png
--------------------------------------------------------------------------------
/img/life.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/haleymt/CrystalQuest/8bd29df2ca65d264fecf37aabd32659ba0a3c153/img/life.png
--------------------------------------------------------------------------------
/img/rm_basic_alien.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/haleymt/CrystalQuest/8bd29df2ca65d264fecf37aabd32659ba0a3c153/img/rm_basic_alien.png
--------------------------------------------------------------------------------
/img/rm_blob_alien.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/haleymt/CrystalQuest/8bd29df2ca65d264fecf37aabd32659ba0a3c153/img/rm_blob_alien.png
--------------------------------------------------------------------------------
/img/rm_bullet_alien.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/haleymt/CrystalQuest/8bd29df2ca65d264fecf37aabd32659ba0a3c153/img/rm_bullet_alien.png
--------------------------------------------------------------------------------
/img/rm_computer_alien.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/haleymt/CrystalQuest/8bd29df2ca65d264fecf37aabd32659ba0a3c153/img/rm_computer_alien.png
--------------------------------------------------------------------------------
/img/rm_cs_alien.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/haleymt/CrystalQuest/8bd29df2ca65d264fecf37aabd32659ba0a3c153/img/rm_cs_alien.png
--------------------------------------------------------------------------------
/img/rm_four_leg_alien.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/haleymt/CrystalQuest/8bd29df2ca65d264fecf37aabd32659ba0a3c153/img/rm_four_leg_alien.png
--------------------------------------------------------------------------------
/img/rm_shooter_alien.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/haleymt/CrystalQuest/8bd29df2ca65d264fecf37aabd32659ba0a3c153/img/rm_shooter_alien.png
--------------------------------------------------------------------------------
/img/rm_x_alien.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/haleymt/CrystalQuest/8bd29df2ca65d264fecf37aabd32659ba0a3c153/img/rm_x_alien.png
--------------------------------------------------------------------------------
/img/shooter_alien.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/haleymt/CrystalQuest/8bd29df2ca65d264fecf37aabd32659ba0a3c153/img/shooter_alien.png
--------------------------------------------------------------------------------
/img/x_alien.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/haleymt/CrystalQuest/8bd29df2ca65d264fecf37aabd32659ba0a3c153/img/x_alien.png
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Crystal Quest
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 | Instructions
21 | Collect all the crystals and escape out the gate
22 | Don't run into any aliens or asteroids!
23 | Use your cherry bombs to clear the screen
24 | Catch the big crystal for a bonus prize
25 |
26 |
27 | Keys
28 | ↑
↓
→
←
or w
s
a
d
to move
29 | r
to stop
30 | space
to shoot
31 | shift
to FLASHBOMB
32 | esc
to return to start menu
33 |
34 | Want to play another game? Try
Snake .
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
76 |
77 |
78 |
--------------------------------------------------------------------------------
/keymaster.js:
--------------------------------------------------------------------------------
1 | // keymaster.js
2 | // (c) 2011-2013 Thomas Fuchs
3 | // keymaster.js may be freely distributed under the MIT license.
4 |
5 | ;(function(global){
6 | var k,
7 | _handlers = {},
8 | _mods = { 16: false, 18: false, 17: false, 91: false },
9 | _scope = 'all',
10 | // modifier keys
11 | _MODIFIERS = {
12 | '⌥': 18, alt: 18, option: 18,
13 | '⌃': 17, ctrl: 17, control: 17,
14 | '⌘': 91, command: 91
15 | },
16 | // special keys
17 | _MAP = {
18 | '⇧': 16, shift: 16,
19 | backspace: 8, tab: 9, clear: 12,
20 | enter: 13, 'return': 13,
21 | esc: 27, escape: 27, space: 32,
22 | left: 37, up: 38,
23 | right: 39, down: 40,
24 | del: 46, 'delete': 46,
25 | home: 36, end: 35,
26 | pageup: 33, pagedown: 34,
27 | ',': 188, '.': 190, '/': 191,
28 | '`': 192, '-': 189, '=': 187,
29 | ';': 186, '\'': 222,
30 | '[': 219, ']': 221, '\\': 220
31 | },
32 | code = function(x){
33 | return _MAP[x] || x.toUpperCase().charCodeAt(0);
34 | },
35 | _downKeys = [];
36 |
37 | for(k=1;k<20;k++) _MAP['f'+k] = 111+k;
38 |
39 | // IE doesn't support Array#indexOf, so have a simple replacement
40 | function index(array, item){
41 | var i = array.length;
42 | while(i--) if(array[i]===item) return i;
43 | return -1;
44 | }
45 |
46 | // for comparing mods before unassignment
47 | function compareArray(a1, a2) {
48 | if (a1.length != a2.length) return false;
49 | for (var i = 0; i < a1.length; i++) {
50 | if (a1[i] !== a2[i]) return false;
51 | }
52 | return true;
53 | }
54 |
55 | var modifierMap = {
56 | 16:'shiftKey',
57 | 18:'altKey',
58 | 17:'ctrlKey',
59 | 91:'metaKey'
60 | };
61 | function updateModifierKey(event) {
62 | for(k in _mods) _mods[k] = event[modifierMap[k]];
63 | };
64 |
65 | // handle keydown event
66 | function dispatch(event) {
67 | var key, handler, k, i, modifiersMatch, scope;
68 | key = event.keyCode;
69 |
70 | if (index(_downKeys, key) == -1) {
71 | _downKeys.push(key);
72 | }
73 |
74 | // if a modifier key, set the key. property to true and return
75 | if(key == 93 || key == 224) key = 91; // right command on webkit, command on Gecko
76 | if(key in _mods) {
77 | _mods[key] = true;
78 | // 'assignKey' from inside this closure is exported to window.key
79 | for(k in _MODIFIERS) if(_MODIFIERS[k] == key) assignKey[k] = true;
80 | return;
81 | }
82 | updateModifierKey(event);
83 |
84 | // see if we need to ignore the keypress (filter() can can be overridden)
85 | // by default ignore key presses if a select, textarea, or input is focused
86 | if(!assignKey.filter.call(this, event)) return;
87 |
88 | // abort if no potentially matching shortcuts found
89 | if (!(key in _handlers)) return;
90 |
91 | scope = getScope();
92 |
93 | // for each potential shortcut
94 | for (i = 0; i < _handlers[key].length; i++) {
95 | handler = _handlers[key][i];
96 |
97 | // see if it's in the current scope
98 | if(handler.scope == scope || handler.scope == 'all'){
99 | // check if modifiers match if any
100 | modifiersMatch = handler.mods.length > 0;
101 | for(k in _mods)
102 | if((!_mods[k] && index(handler.mods, +k) > -1) ||
103 | (_mods[k] && index(handler.mods, +k) == -1)) modifiersMatch = false;
104 | // call the handler and stop the event if neccessary
105 | if((handler.mods.length == 0 && !_mods[16] && !_mods[18] && !_mods[17] && !_mods[91]) || modifiersMatch){
106 | if(handler.method(event, handler)===false){
107 | if(event.preventDefault) event.preventDefault();
108 | else event.returnValue = false;
109 | if(event.stopPropagation) event.stopPropagation();
110 | if(event.cancelBubble) event.cancelBubble = true;
111 | }
112 | }
113 | }
114 | }
115 | };
116 |
117 | // unset modifier keys on keyup
118 | function clearModifier(event){
119 | var key = event.keyCode, k,
120 | i = index(_downKeys, key);
121 |
122 | // remove key from _downKeys
123 | if (i >= 0) {
124 | _downKeys.splice(i, 1);
125 | }
126 |
127 | if(key == 93 || key == 224) key = 91;
128 | if(key in _mods) {
129 | _mods[key] = false;
130 | for(k in _MODIFIERS) if(_MODIFIERS[k] == key) assignKey[k] = false;
131 | }
132 | };
133 |
134 | function resetModifiers() {
135 | for(k in _mods) _mods[k] = false;
136 | for(k in _MODIFIERS) assignKey[k] = false;
137 | };
138 |
139 | // parse and assign shortcut
140 | function assignKey(key, scope, method){
141 | var keys, mods;
142 | keys = getKeys(key);
143 | if (method === undefined) {
144 | method = scope;
145 | scope = 'all';
146 | }
147 |
148 | // for each shortcut
149 | for (var i = 0; i < keys.length; i++) {
150 | // set modifier keys if any
151 | mods = [];
152 | key = keys[i].split('+');
153 | if (key.length > 1){
154 | mods = getMods(key);
155 | key = [key[key.length-1]];
156 | }
157 | // convert to keycode and...
158 | key = key[0]
159 | key = code(key);
160 | // ...store handler
161 | if (!(key in _handlers)) _handlers[key] = [];
162 | _handlers[key].push({ shortcut: keys[i], scope: scope, method: method, key: keys[i], mods: mods });
163 | }
164 | };
165 |
166 | // unbind all handlers for given key in current scope
167 | function unbindKey(key, scope) {
168 | var multipleKeys, keys,
169 | mods = [],
170 | i, j, obj;
171 |
172 | multipleKeys = getKeys(key);
173 |
174 | for (j = 0; j < multipleKeys.length; j++) {
175 | keys = multipleKeys[j].split('+');
176 |
177 | if (keys.length > 1) {
178 | mods = getMods(keys);
179 | }
180 |
181 | key = keys[keys.length - 1];
182 | key = code(key);
183 |
184 | if (scope === undefined) {
185 | scope = getScope();
186 | }
187 | if (!_handlers[key]) {
188 | return;
189 | }
190 | for (i = 0; i < _handlers[key].length; i++) {
191 | obj = _handlers[key][i];
192 | // only clear handlers if correct scope and mods match
193 | if (obj.scope === scope && compareArray(obj.mods, mods)) {
194 | _handlers[key][i] = {};
195 | }
196 | }
197 | }
198 | };
199 |
200 | // Returns true if the key with code 'keyCode' is currently down
201 | // Converts strings into key codes.
202 | function isPressed(keyCode) {
203 | if (typeof(keyCode)=='string') {
204 | keyCode = code(keyCode);
205 | }
206 | return index(_downKeys, keyCode) != -1;
207 | }
208 |
209 | function getPressedKeyCodes() {
210 | return _downKeys.slice(0);
211 | }
212 |
213 | function filter(event){
214 | var tagName = (event.target || event.srcElement).tagName;
215 | // ignore keypressed in any elements that support keyboard data input
216 | return !(tagName == 'INPUT' || tagName == 'SELECT' || tagName == 'TEXTAREA');
217 | }
218 |
219 | // initialize key. to false
220 | for(k in _MODIFIERS) assignKey[k] = false;
221 |
222 | // set current scope (default 'all')
223 | function setScope(scope){ _scope = scope || 'all' };
224 | function getScope(){ return _scope || 'all' };
225 |
226 | // delete all handlers for a given scope
227 | function deleteScope(scope){
228 | var key, handlers, i;
229 |
230 | for (key in _handlers) {
231 | handlers = _handlers[key];
232 | for (i = 0; i < handlers.length; ) {
233 | if (handlers[i].scope === scope) handlers.splice(i, 1);
234 | else i++;
235 | }
236 | }
237 | };
238 |
239 | // abstract key logic for assign and unassign
240 | function getKeys(key) {
241 | var keys;
242 | key = key.replace(/\s/g, '');
243 | keys = key.split(',');
244 | if ((keys[keys.length - 1]) == '') {
245 | keys[keys.length - 2] += ',';
246 | }
247 | return keys;
248 | }
249 |
250 | // abstract mods logic for assign and unassign
251 | function getMods(key) {
252 | var mods = key.slice(0, key.length - 1);
253 | for (var mi = 0; mi < mods.length; mi++)
254 | mods[mi] = _MODIFIERS[mods[mi]];
255 | return mods;
256 | }
257 |
258 | // cross-browser events
259 | function addEvent(object, event, method) {
260 | if (object.addEventListener)
261 | object.addEventListener(event, method, false);
262 | else if(object.attachEvent)
263 | object.attachEvent('on'+event, function(){ method(window.event) });
264 | };
265 |
266 | // set the handlers globally on document
267 | addEvent(document, 'keydown', function(event) { dispatch(event) }); // Passing _scope to a callback to ensure it remains the same by execution. Fixes #48
268 | addEvent(document, 'keyup', clearModifier);
269 |
270 | // reset modifiers to false whenever the window is (re)focused.
271 | addEvent(window, 'focus', resetModifiers);
272 |
273 | // store previously defined key
274 | var previousKey = global.key;
275 |
276 | // restore previously defined key and return reference to our key object
277 | function noConflict() {
278 | var k = global.key;
279 | global.key = previousKey;
280 | return k;
281 | }
282 |
283 | // set window.key and window.key.set/get/deleteScope, and the default filter
284 | global.key = assignKey;
285 | global.key.setScope = setScope;
286 | global.key.getScope = getScope;
287 | global.key.deleteScope = deleteScope;
288 | global.key.filter = filter;
289 | global.key.isPressed = isPressed;
290 | global.key.getPressedKeyCodes = getPressedKeyCodes;
291 | global.key.noConflict = noConflict;
292 | global.key.unbind = unbindKey;
293 |
294 | if(typeof module !== 'undefined') module.exports = assignKey;
295 |
296 | })(this);
297 |
--------------------------------------------------------------------------------
/lib/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/haleymt/CrystalQuest/8bd29df2ca65d264fecf37aabd32659ba0a3c153/lib/.DS_Store
--------------------------------------------------------------------------------
/lib/CSAlien.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | if (typeof CrystalQuest === "undefined") {
3 | window.CrystalQuest = {};
4 | }
5 | var MovingObject = window.CrystalQuest.MovingObject;
6 |
7 | CrystalQuest.CSAlien = function (options) {
8 | MovingObject.call(this, options);
9 | this.radius = 12;
10 | this.img = 'img/cs_alien.png';
11 | this.bulletImg = 'img/alien_bullet2.png';
12 | };
13 |
14 | window.CrystalQuest.Util.inherits(CrystalQuest.CSAlien, MovingObject);
15 |
16 | CrystalQuest.CSAlien.prototype.startIntervals = function() {
17 | var that = this;
18 | this.dirInterval = setInterval( function () {
19 | that.vel = window.CrystalQuest.Util.randomVec(3);
20 | if (that.wave.gameOver) {
21 | that.clearIntervals();
22 | }
23 | }, 500);
24 | this.shootInterval = setInterval( function () {
25 | var bulletVel = window.CrystalQuest.Util.randomVec(2);
26 | var bullet = new window.CrystalQuest.AlienBullet({
27 | pos: that.pos,
28 | vel: bulletVel,
29 | wave: that.wave,
30 | img: that.bulletImg
31 | });
32 | if (that.pos[0] > 12 && that.pos[0] < (780 - 12)) {
33 | that.wave.alienBullets.push(bullet);
34 | }
35 | if (that.wave.gameOver || that.wave.indexOf(that) < 0) {
36 | that.clearIntervals();
37 | }
38 | }, 500);
39 | };
40 | })();
41 |
--------------------------------------------------------------------------------
/lib/XSAlien.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | if (typeof CrystalQuest === "undefined") {
3 | window.CrystalQuest = {};
4 | }
5 | var MovingObject = window.CrystalQuest.MovingObject;
6 |
7 | CrystalQuest.XSAlien = function (options) {
8 | MovingObject.call(this, options);
9 | this.radius = 12;
10 | this.img = 'img/x_alien.png';
11 | this.bulletImg = 'img/alien_bullet3.png';
12 | };
13 |
14 | window.CrystalQuest.Util.inherits(CrystalQuest.XSAlien, MovingObject);
15 |
16 | CrystalQuest.XSAlien.prototype.startIntervals = function() {
17 | var that = this;
18 | this.dirInterval = setInterval( function () {
19 | that.vel = window.CrystalQuest.Util.randomVec(3);
20 | if (that.wave.gameOver) {
21 | that.clearIntervals();
22 | }
23 | }, 500);
24 |
25 | this.shootInterval = setInterval( function () {
26 | var bulletVel = [
27 | that.vel[0] * 2, that.vel[1] * 2
28 | ];
29 | var bullet = new window.CrystalQuest.AlienBullet({
30 | pos: that.pos,
31 | vel: bulletVel,
32 | wave: that.wave,
33 | img: that.bulletImg
34 | });
35 | if (that.pos[0] > 12 && that.pos[0] < (780 - 12)) {
36 | that.wave.alienBullets.push(bullet);
37 | }
38 | if (that.wave.gameOver || that.wave.indexOf(that) < 0) {
39 | that.clearIntervals();
40 | }
41 | }, 200);
42 | }
43 | })();
44 |
--------------------------------------------------------------------------------
/lib/alienBullet.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | if (typeof CrystalQuest === "undefined") {
3 | window.CrystalQuest = {};
4 | }
5 |
6 | var MovingObject = window.CrystalQuest.MovingObject;
7 | var Wave = window.CrystalQuest.Wave;
8 |
9 | var AlienBullet = CrystalQuest.AlienBullet = function (options) {
10 | MovingObject.call(this, options);
11 | this.radius = 4;
12 | this.img = options.img;
13 | };
14 |
15 | window.CrystalQuest.Util.inherits(AlienBullet, MovingObject);
16 | CrystalQuest.AlienBullet.prototype.isBounceable = function () {
17 | return false;
18 | };
19 |
20 | })();
21 |
--------------------------------------------------------------------------------
/lib/asteroid.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | if (typeof CrystalQuest === "undefined") {
3 | window.CrystalQuest = {};
4 | }
5 |
6 | var Asteroid = CrystalQuest.Asteroid = function (options) {
7 | this.pos = options.pos;
8 | this.wave = options.wave;
9 | this.color = "#FF7519";
10 | this.width = 20;
11 | };
12 |
13 | Asteroid.prototype.draw = function(ctx) {
14 | ctx.fillStyle = this.color;
15 | ctx.fillRect(this.pos[0], this.pos[1], this.width, this.width);
16 |
17 | ctx.fill();
18 | };
19 |
20 |
21 | })();
22 |
--------------------------------------------------------------------------------
/lib/basicAlien.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | if (typeof CrystalQuest === "undefined") {
3 | window.CrystalQuest = {};
4 | }
5 | var MovingObject = window.CrystalQuest.MovingObject;
6 |
7 | CrystalQuest.BasicAlien = function (options) {
8 | MovingObject.call(this, options);
9 | this.radius = 14;
10 | this.img = 'img/basic_alien_sp.png';
11 | };
12 |
13 | window.CrystalQuest.Util.inherits(CrystalQuest.BasicAlien, MovingObject);
14 |
15 | CrystalQuest.BasicAlien.prototype.startIntervals = function() {
16 | var that = this;
17 | this.dirInterval = setInterval( function () {
18 | that.vel = window.CrystalQuest.Util.randomVec(3);
19 | if (that.wave.gameOver) {
20 | that.clearIntervals();
21 | }
22 | }, 500);
23 | };
24 |
25 | })();
26 |
--------------------------------------------------------------------------------
/lib/bigCrystal.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | if (typeof CrystalQuest === "undefined") {
3 | window.CrystalQuest = {};
4 | }
5 | var MovingObject = window.CrystalQuest.MovingObject;
6 |
7 | var BigCrystal = CrystalQuest.BigCrystal = function (options) {
8 | MovingObject.call(this, options);
9 | this.radius = 20;
10 | this.img = 'img/big_crystal.png';
11 | this.num = Math.floor(Math.random() * (30000) + 2000);
12 | };
13 |
14 | window.CrystalQuest.Util.inherits(CrystalQuest.BigCrystal, MovingObject);
15 |
16 | })();
17 |
--------------------------------------------------------------------------------
/lib/blobAlien.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | if (typeof CrystalQuest === "undefined") {
3 | window.CrystalQuest = {};
4 | }
5 | var MovingObject = window.CrystalQuest.MovingObject;
6 |
7 | CrystalQuest.BlobAlien = function (options) {
8 | MovingObject.call(this, options);
9 | this.radius = 20;
10 | this.img = 'img/blob_alien.png';
11 | };
12 |
13 | window.CrystalQuest.Util.inherits(CrystalQuest.BlobAlien, MovingObject);
14 |
15 | CrystalQuest.BlobAlien.prototype.startIntervals = function() {
16 | var that = this;
17 | this.dirInterval = setInterval( function () {
18 | that.vel = window.CrystalQuest.Util.randomVec(1);
19 | if (that.wave.gameOver) {
20 | that.clearIntervals();
21 | }
22 | }, 500);
23 | };
24 | })();
25 |
--------------------------------------------------------------------------------
/lib/bomb.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | if (typeof CrystalQuest === "undefined") {
3 | window.CrystalQuest = {};
4 | }
5 |
6 | var Bomb = CrystalQuest.Bomb = function (options) {
7 | this.pos = options.pos;
8 | this.wave = options.wave;
9 | this.width = 15;
10 | };
11 |
12 | Bomb.prototype.draw = function(ctx) {
13 | var img = new Image();
14 | x = this.pos[0];
15 | y = this.pos[1];
16 | img.src = 'img/bomb.png';
17 | ctx.drawImage(img, x, y, 15, 15);
18 | };
19 |
20 |
21 | })();
22 |
--------------------------------------------------------------------------------
/lib/bullet.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | if (typeof CrystalQuest === "undefined") {
3 | window.CrystalQuest = {};
4 | }
5 |
6 | var MovingObject = window.CrystalQuest.MovingObject;
7 | var Wave = window.CrystalQuest.Wave;
8 |
9 | var Bullet = CrystalQuest.Bullet = function (options) {
10 | MovingObject.call(this, options);
11 | this.color = "#FFFFFF";
12 | this.radius = 3;
13 | };
14 |
15 | Bullet.SPEED = 3;
16 |
17 | window.CrystalQuest.Util.inherits(Bullet, MovingObject);
18 | CrystalQuest.Bullet.prototype.isBounceable = function () {
19 | return false;
20 | };
21 |
22 | })();
23 |
--------------------------------------------------------------------------------
/lib/bulletAlien.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | if (typeof CrystalQuest === "undefined") {
3 | window.CrystalQuest = {};
4 | }
5 | var MovingObject = window.CrystalQuest.MovingObject;
6 |
7 | CrystalQuest.BulletAlien = function (options) {
8 | MovingObject.call(this, options);
9 | this.radius = 7;
10 | this.img = 'img/bullet_alien.png';
11 | };
12 |
13 | window.CrystalQuest.Util.inherits(CrystalQuest.BulletAlien, MovingObject);
14 |
15 | CrystalQuest.BulletAlien.prototype.startIntervals = function() {
16 | var that = this;
17 | this.dirInterval = setInterval( function () {
18 | that.vel = window.CrystalQuest.Util.randomVec(4);
19 | if (that.wave.gameOver) {
20 | that.clearIntervals();
21 | }
22 | }, 800);
23 | }
24 | })();
25 |
--------------------------------------------------------------------------------
/lib/clock.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | if (typeof CrystalQuest === "undefined") {
3 | window.CrystalQuest = {};
4 | }
5 |
6 | var Clock = CrystalQuest.Clock = function() {
7 | this.minutes = 0
8 | this.seconds = 0
9 | }
10 |
11 | Clock.prototype.stop = function() {
12 | clearInterval(this.interval);
13 | this.interval = null;
14 | };
15 |
16 | Clock.prototype.printTime = function() {
17 | if (this.minutes > 9) {
18 | if (this.seconds > 9) {
19 | $('#time').html(this.minutes + ":" + this.seconds);
20 | } else {
21 | $('#time').html(this.minutes + ":0" + this.seconds);
22 | }
23 | } else {
24 | if (this.seconds > 9) {
25 | $('#time').html("0" + this.minutes + ":" + this.seconds);
26 | } else {
27 | $('#time').html("0" + this.minutes + ":0" + this.seconds);
28 | }
29 | }
30 | };
31 |
32 | Clock.prototype.run = function () {
33 | this.printTime();
34 | var that = this;
35 | this.interval = setInterval(function () {
36 | that._tick();
37 | }, 1000);
38 | };
39 |
40 | Clock.prototype._tick = function () {
41 | var seconds = this.seconds + 1;
42 | if (seconds >= 60) {
43 | this.minutes += 1;
44 | this.seconds = 0 + (seconds - 60);
45 | } else {
46 | this.seconds += 1;
47 | }
48 | this.printTime();
49 | };
50 |
51 | })();
52 |
--------------------------------------------------------------------------------
/lib/computerAlien.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | if (typeof CrystalQuest === "undefined") {
3 | window.CrystalQuest = {};
4 | }
5 | var MovingObject = window.CrystalQuest.MovingObject;
6 |
7 | CrystalQuest.ComputerAlien = function (options) {
8 | MovingObject.call(this, options);
9 | this.radius = 12;
10 | this.img = 'img/computer_alien.png';
11 | };
12 |
13 | window.CrystalQuest.Util.inherits(CrystalQuest.ComputerAlien, MovingObject);
14 |
15 | CrystalQuest.ComputerAlien.prototype.startIntervals = function() {
16 | var that = this;
17 | this.dirInterval = setInterval( function () {
18 | that.vel = window.CrystalQuest.Util.randomVec(5);
19 | if (!that.wave || that.wave.gameOver) {
20 | that.clearIntervals();
21 | }
22 | }, 500);
23 | this.hashInterval = setInterval( function () {
24 | var hash = new window.CrystalQuest.ComputerHash({
25 | pos: that.pos,
26 | wave: that.wave
27 | });
28 | if (that.pos[0] > 12 && that.pos[0] < (780 - 12) && that.wave) {
29 | that.wave.asteroids.push(hash);
30 | }
31 | if (!that.wave || that.wave.gameOver) {
32 | that.clearIntervals();
33 | }
34 | }, 5000);
35 | }
36 |
37 | })();
38 |
--------------------------------------------------------------------------------
/lib/computerHash.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | if (typeof CrystalQuest === "undefined") {
3 | window.CrystalQuest = {};
4 | }
5 |
6 | var ComputerHash = CrystalQuest.ComputerHash = function (options) {
7 | this.pos = options.pos;
8 | this.wave = options.wave;
9 | this.width = 15;
10 | };
11 |
12 | ComputerHash.prototype.draw = function(ctx) {
13 | var img = new Image();
14 | x = this.pos[0] - 7.5;
15 | y = this.pos[1] - 7.5;
16 | img.src = 'img/computer_hash.png';
17 | ctx.drawImage(img, x, y, 15, 15);
18 | };
19 |
20 | })();
21 |
--------------------------------------------------------------------------------
/lib/crystal.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | if (typeof CrystalQuest === "undefined") {
3 | window.CrystalQuest = {};
4 | }
5 |
6 | var Crystal = CrystalQuest.Crystal = function (options) {
7 | this.pos = options.pos;
8 | this.wave = options.wave;
9 | this.width = 15;
10 | };
11 |
12 | Crystal.prototype.draw = function(ctx) {
13 | var img = new Image();
14 | x = this.pos[0] - 7.5;
15 | y = this.pos[1] - 7.5;
16 | img.src = 'img/crystal.png';
17 | ctx.drawImage(img, x, y, 15, 15);
18 | };
19 |
20 | })();
21 |
--------------------------------------------------------------------------------
/lib/crystalPoints.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | if (typeof CrystalQuest === "undefined") {
3 | window.CrystalQuest = {};
4 | }
5 |
6 | CrystalQuest.CrystalPoints = function (options) {
7 | this.pos = options.pos;
8 | this.wave = options.wave;
9 | this.num = options.num;
10 | this.width = 20;
11 | };
12 |
13 | CrystalQuest.CrystalPoints.prototype.draw = function(ctx) {
14 | ctx.fillStyle = "yellow";
15 | ctx.font = "10pt Arial";
16 | ctx.fillText(this.num, this.pos[0], this.pos[1]);
17 | ctx.fill();
18 | };
19 |
20 | })();
21 |
--------------------------------------------------------------------------------
/lib/fourLegAlien.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | if (typeof CrystalQuest === "undefined") {
3 | window.CrystalQuest = {};
4 | }
5 | var MovingObject = window.CrystalQuest.MovingObject;
6 |
7 | CrystalQuest.FourLegAlien = function (options) {
8 | MovingObject.call(this, options);
9 | this.radius = 12;
10 | this.img = 'img/four_leg_alien.png';
11 | };
12 |
13 | window.CrystalQuest.Util.inherits(CrystalQuest.FourLegAlien, MovingObject);
14 |
15 | CrystalQuest.FourLegAlien.prototype.startIntervals = function() {
16 | var that = this;
17 | this.dirInterval = setInterval( function () {
18 | that.vel = window.CrystalQuest.Util.randomVec(3);
19 | if (that.wave.gameOver) {
20 | that.clearIntervals();
21 | }
22 | }, 500);
23 |
24 | this.shootInterval = setInterval( function () {
25 | var bulletVel = window.CrystalQuest.Util.randomVec(3);
26 | var bullet = new window.CrystalQuest.BulletAlien({
27 | pos: that.pos,
28 | vel: bulletVel,
29 | wave: that.wave
30 | });
31 | if (that.pos[0] > 12 && that.pos[0] < (780 - 12)) {
32 | that.wave.aliens.push(bullet);
33 | }
34 | if (that.wave.gameOver || that.wave.indexOf(that) < 0) {
35 | that.clearIntervals();
36 | }
37 | }, 2000);
38 | }
39 | })();
40 |
--------------------------------------------------------------------------------
/lib/game.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | if (typeof CrystalQuest === "undefined") {
3 | window.CrystalQuest = {};
4 | }
5 |
6 | var Game = window.CrystalQuest.Game = function (xDim, yDim, ctx, audioCtx) {
7 | this.X_DIM = xDim;
8 | this.Y_DIM = yDim;
9 | this.ctx = ctx;
10 | this.audioCtx = audioCtx;
11 | this.timeBonus = 0;
12 | this.counter = 0;
13 | // this.database = {};
14 | this.database = new Firebase("https://luminous-inferno-7080.firebaseio.com/web/data");
15 |
16 | $(document).keyup(function (event) {
17 | event.preventDefault();
18 | if (event.keyCode === 13) {
19 | $('#start').click();
20 | $('#next-level').click();
21 | }
22 | }.bind(this))
23 | }
24 |
25 | Game.prototype.getHighScores = function(cb) {
26 | var scores = this.database.child('highScores');
27 | scores.orderByChild('score').limitToLast(10).once("value", function(snapshot) {
28 | var val = snapshot.val();
29 | var result = [];
30 | for (var key in val) {
31 | result.push(val[key]);
32 | }
33 | result.sort(function(a, b) {
34 | return a.score > b.score;
35 | });
36 |
37 | if (cb) {
38 | cb(result);
39 | }
40 | }, function (errorObject) {
41 | console.log("The read failed: " + errorObject.code);
42 | });
43 | return scores;
44 | }
45 |
46 | Game.prototype.bindKeyHandlers = function(wave) {
47 | var ship = wave.ship
48 | key('up', function(e) { e.preventDefault(); ship.power([0,-2]) });
49 | key('down', function(e) { e.preventDefault(); ship.power([0,2]) });
50 | key('right', function (e) { e.preventDefault(); ship.power([2,0]) });
51 | key('left', function (e) { e.preventDefault(); ship.power([-2,0]) });
52 | key('w', function(e) { e.preventDefault(); ship.power([0,-2]) });
53 | key('s', function(e) { e.preventDefault(); ship.power([0,2]) });
54 | key('d', function (e) { e.preventDefault(); ship.power([2,0]) });
55 | key('a', function (e) { e.preventDefault(); ship.power([-2,0]) });
56 | key('r', function(e) { e.preventDefault(); ship.power([0,0]) });
57 | key('space', function(e) { e.preventDefault(); ship.fireBullet() });
58 | };
59 |
60 | Game.prototype.playSound = function(sound) {
61 | var file = Game.SOUNDS[sound];
62 | var request = new XMLHttpRequest();
63 | request.open('GET', file, true);
64 | request.responseType = 'arraybuffer';
65 |
66 | var _this = this;
67 | request.onload = function() {
68 | var audioData = request.response;
69 | _this.audioCtx.decodeAudioData(audioData, function(buffer) {
70 | var src = _this.audioCtx.createBufferSource();
71 |
72 | src.buffer = buffer;
73 | src.connect(this.audioCtx.destination);
74 | src.start(0);
75 | });
76 | }
77 |
78 | request.send();
79 | };
80 |
81 | Game.prototype.stop = function() {
82 | if (this.interval) {
83 | clearInterval(this.interval);
84 | this.interval = null;
85 | }
86 | };
87 |
88 | Game.prototype.win = function() {
89 | var timeTaken = $('#time').text();
90 | var seconds = (parseInt(timeTaken.split(":")[0]) / 60) + parseInt(timeTaken.split(":")[1]);
91 | var bonus = Math.floor((30 - seconds) * 500);
92 | this.timeBonus = bonus > 0 ? bonus : 0;
93 |
94 | if (this.counter === Game.WAVES.length - 1) {
95 | this.counter = 0;
96 | var total = this.timeBonus + parseInt($('#score').text());
97 | var content = "YOU WON!!! Wave number " + $('#wave').text() + " completed
Time taken: " + timeTaken + "
Time bonus: " + this.timeBonus + "
Total Score: " + total + " Play Again? "
98 | $('#controls').append(content);
99 | var _this = this;
100 | this.getHighScores(function(scores) {
101 | if (scores.length < 10 || scores[0].score < score) {
102 | _this.enterHighScore(total);
103 | }
104 | });
105 | } else {
106 | var content = "Wave number " + $('#wave').text() + " completed
Time taken: " + timeTaken + "
Time bonus: " + this.timeBonus + "
Next Level ";
107 | $('#controls').append(content);
108 | }
109 | };
110 |
111 | Game.prototype.lose = function() {
112 | this.counter = 0;
113 | var score = parseInt($('#score').text());
114 | var _this = this;
115 | this.getHighScores(function(scores) {
116 | if (scores.length < 10 || scores[0].score < score) {
117 | _this.enterHighScore(score);
118 | _this.showHighScores();
119 | } else {
120 | var content = "Game Over
Restart "
121 | $('#controls').append(content)
122 | }
123 | });
124 | };
125 |
126 | Game.prototype.showIntro = function () {
127 | $('#status-bar').empty();
128 | var str = "CRYSTAL QUEST Start High Scores Press enter
or click Start to start "
129 | $('#controls').append(str);
130 | };
131 |
132 | Game.prototype.showHighScores = function () {
133 | $('#status-bar').empty();
134 | var str = "HIGH SCORES "
135 | $('#controls').append(str);
136 | this.getHighScores(function(scores) {
137 | scores.forEach(function(score) {
138 | var scoreStr = "" + score['name'] + " " + score['score'] + " "
139 | $('ul').prepend(scoreStr);
140 | });
141 | });
142 | };
143 |
144 | Game.prototype.enterHighScore = function (score) {
145 | var name = prompt("You got a new high score! Enter your initials: ");
146 | if ((name !== "") && (name !== null)) {
147 | var scoresRef = this.database.child('highScores');
148 | scoresRef.push({ name: name.slice(0,3), score: score });
149 | }
150 | }
151 |
152 | Game.prototype.startLevel = function () {
153 | $('#controls').empty();
154 |
155 | if (this.counter === 0) {
156 | $('#status-bar').empty();
157 | $('#status-bar').append('Score: 0 Wave: 1 Time: 00:00 ');
158 | } else {
159 | $('#wave').html(this.counter + 1)
160 | var score = parseInt($('#score').text()) + this.timeBonus;
161 | $('#score').html(score);
162 | this.ctx.clearRect(0, 0, this.X_DIM, this.Y_DIM);
163 | }
164 | var wave = new window.CrystalQuest.Wave(this.X_DIM, this.Y_DIM, this, Game.WAVES[this.counter]);
165 | var _this = this;
166 |
167 | this.interval = setInterval( function () {
168 | var step = wave.step();
169 | if (step === "lost" ) {
170 | _this.endLevel(wave, 'lost');
171 | } else if (step === "won") {
172 | _this.endLevel(wave, 'won');
173 | } else if (step === "quit") {
174 | _this.endLevel(wave, 'quit');
175 | } else {
176 | wave.draw(this.ctx);
177 | }
178 | }, 10);
179 |
180 | this.counter++;
181 | this.bindKeyHandlers(wave);
182 | };
183 |
184 | Game.prototype.endLevel = function (wave, ending) {
185 | this.stop();
186 | this.ctx.clearRect(0, 0, this.X_DIM, this.Y_DIM);
187 |
188 | wave.aliens.forEach( function (alien) {
189 | wave.remove(wave.aliens, alien);
190 | }.bind(wave))
191 | wave.aliens = [];
192 |
193 | if (ending === 'lost' || ending === 'quit') {
194 | this.lose();
195 | } else {
196 | this.win();
197 | }
198 | };
199 |
200 | Game.prototype.run = function () {
201 | this.showIntro();
202 |
203 | $('#controls').on('click', '#start', this.startLevel.bind(this));
204 | $('#controls').on('click', '#next-level', this.startLevel.bind(this));
205 |
206 | $('#controls').on('click', '#high-scores', function () {
207 | $('#controls').empty();
208 | this.ctx.clearRect(0, 0, this.X_DIM, this.Y_DIM);
209 | this.showHighScores();
210 | }.bind(this))
211 |
212 | $('#controls').on('click', "#start-menu", function () {
213 | $('#controls').empty();
214 | this.ctx.clearRect(0, 0, this.X_DIM, this.Y_DIM);
215 | this.showIntro();
216 | }.bind(this))
217 |
218 | };
219 |
220 | Game.WAVE_ONE = {
221 | numAsteroids: 0,
222 | numBombs: 0,
223 | numCrystals: 10,
224 | numBasicAliens: 4,
225 | numBigCrystals: 0,
226 | numPoints: 0,
227 | // numAsteroids: 10,
228 | // numBombs: 1,
229 | // numCrystals: 30,
230 | // numCShooterAliens: 6,
231 | // numBasicAliens: 2,
232 | // numShooterAliens: 4,
233 | // numComputerAliens: 2,
234 | // numBigCrystals: 0,
235 | // numPoints: 1
236 | };
237 |
238 | Game.WAVE_TWO = {
239 | numAsteroids: 0,
240 | numBombs: 0,
241 | numCrystals: 12,
242 | numBasicAliens: 1,
243 | numBigCrystals: 1,
244 | numPoints: 0
245 | };
246 |
247 | Game.WAVE_THREE = {
248 | numAsteroids: 2,
249 | numBombs: 0,
250 | numCrystals: 15,
251 | numShooterAliens: 2,
252 | numBigCrystals: 1,
253 | numPoints: 0
254 | };
255 |
256 | Game.WAVE_FOUR = {
257 | numAsteroids: 5,
258 | numBombs: 0,
259 | numCrystals: 15,
260 | numShooterAliens: 4,
261 | numBigCrystals: 0,
262 | numPoints: 2
263 | };
264 |
265 | Game.WAVE_FIVE = {
266 | numAsteroids: 5,
267 | numBombs: 1,
268 | numCrystals: 20,
269 | numBigCrystals: 0,
270 | numPoints: 0
271 | };
272 |
273 | Game.WAVE_SIX = {
274 | numAsteroids: 10,
275 | numBombs: 1,
276 | numCrystals: 20,
277 | numBlobAliens: 3,
278 | numBasicAliens: 1,
279 | numBigCrystals: 1,
280 | numPoints: 1
281 | };
282 |
283 | Game.WAVE_SEVEN = {
284 | numAsteroids: 10,
285 | numBombs: 0,
286 | numCrystals: 25,
287 | numComputerAliens: 1,
288 | numBigCrystals: 1,
289 | numPoints: 3
290 | };
291 |
292 | Game.WAVE_EIGHT = {
293 | numAsteroids: 10,
294 | numBombs: 1,
295 | numCrystals: 25,
296 | numComputerAliens: 5,
297 | numBasicAliens: 1,
298 | numBigCrystals: 0,
299 | numPoints: 3
300 | };
301 |
302 | Game.WAVE_NINE = {
303 | numAsteroids: 10,
304 | numBombs: 0,
305 | numCrystals: 30,
306 | numCShooterAliens: 6,
307 | numBigCrystals: 1,
308 | numPoints: 2
309 | };
310 |
311 | Game.WAVE_TEN = {
312 | numAsteroids: 10,
313 | numBombs: 1,
314 | numCrystals: 30,
315 | numCShooterAliens: 6,
316 | numBasicAliens: 2,
317 | numShooterAliens: 4,
318 | numComputerAliens: 2,
319 | numBigCrystals: 0,
320 | numPoints: 1
321 | };
322 |
323 | Game.WAVE_ELEVEN = {
324 | numAsteroids: 10,
325 | numBombs: 1,
326 | numCrystals: 30,
327 | numFourLegsAliens: 2,
328 | numBigCrystals: 1,
329 | numPoints: 1
330 | };
331 |
332 | Game.WAVE_TWELVE = {
333 | numAsteroids: 10,
334 | numBombs: 1,
335 | numCrystals: 30,
336 | numBlobAliens: 2,
337 | numBasicAliens: 1,
338 | numFourLegsAliens: 5,
339 | numCShooterAliens: 1,
340 | numShooterAliens: 1,
341 | numBigCrystals: 0,
342 | numPoints: 2
343 | };
344 |
345 | Game.WAVE_THIRTEEN = {
346 | numAsteroids: 10,
347 | numBombs: 2,
348 | numCrystals: 30,
349 | numXShooterAliens: 4,
350 | numBigCrystals: 0,
351 | numPoints: 2
352 | };
353 |
354 | Game.WAVE_FOURTEEN = {
355 | numAsteroids: 10,
356 | numBombs: 2,
357 | numCrystals: 30,
358 | numXShooterAliens: 13,
359 | numBasicAliens: 2,
360 | numFourLegsAliens: 2,
361 | numShooterAliens: 1,
362 | numBlobAliens: 2,
363 | numCShooterAliens: 1,
364 | numBigCrystals: 0,
365 | numPoints: 1
366 | };
367 |
368 | // Wave fifteen seeding is temporary. More levels to come.
369 | Game.WAVE_FIFTEEN = {
370 | numAsteroids: 15,
371 | numBombs: 2,
372 | numCrystals: 30,
373 | numBlobAliens: 7,
374 | numBasicAliens: 2,
375 | numXShooterAliens: 6,
376 | numComputerAliens: 5,
377 | numFourLegsAliens: 2,
378 | numCShooterAliens: 5,
379 | numShooterAliens: 2,
380 | numBigCrystals: 2,
381 | numPoints: 5
382 | };
383 |
384 | Game.WAVES = [
385 | Game.WAVE_ONE,
386 | Game.WAVE_TWO,
387 | Game.WAVE_THREE,
388 | Game.WAVE_FOUR,
389 | Game.WAVE_FIVE,
390 | Game.WAVE_SIX,
391 | Game.WAVE_SEVEN,
392 | Game.WAVE_EIGHT,
393 | Game.WAVE_NINE,
394 | Game.WAVE_TEN,
395 | Game.WAVE_ELEVEN,
396 | Game.WAVE_TWELVE,
397 | Game.WAVE_THIRTEEN,
398 | Game.WAVE_FOURTEEN,
399 | Game.WAVE_FIFTEEN
400 | ];
401 |
402 | Game.SOUNDS = {
403 | bullet: 'sounds/laser.wav',
404 | wall: 'sounds/reverbLaser.wav',
405 | crystal: 'sounds/crystal.wav',
406 | success: 'sounds/success.mp3',
407 | levelSuccess: 'sounds/levelSuccess.wav',
408 | tristeza: 'sounds/tristeza.wav',
409 | };
410 |
411 | })();
412 |
--------------------------------------------------------------------------------
/lib/gate.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | if (typeof CrystalQuest === "undefined") {
3 | window.CrystalQuest = {};
4 | }
5 |
6 | var Gate = CrystalQuest.Gate = function (options) {
7 | this.wave = options.wave;
8 | this.color = "#FFFFFF";
9 | };
10 |
11 | Gate.prototype.draw = function(ctx) {
12 | height = 500
13 | middle = (780 / 2)
14 |
15 | ctx.fillStyle = this.color;
16 | ctx.beginPath();
17 | ctx.moveTo((middle - 30), height);
18 | ctx.lineTo((middle - 60), height);
19 | ctx.lineTo((middle - 30), height - 20);
20 | ctx.fill();
21 |
22 | ctx.fillStyle = this.color;
23 | ctx.beginPath();
24 | ctx.moveTo((middle + 30), height);
25 | ctx.lineTo((middle + 60), height);
26 | ctx.lineTo((middle + 30), height - 20);
27 | ctx.fill();
28 |
29 | if (this.wave.crystals.length === 0) {
30 | var strokeColor = 'black'
31 | } else {
32 | strokeColor = "#FFFFFF"
33 | }
34 | ctx.beginPath();
35 | ctx.moveTo((middle - 30), height - 18)
36 | ctx.lineTo((middle + 30), height - 18);
37 | ctx.lineWidth = 4;
38 | ctx.strokeStyle = strokeColor;
39 | ctx.stroke();
40 | };
41 |
42 |
43 | })();
44 |
--------------------------------------------------------------------------------
/lib/laserAlien.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | if (typeof CrystalQuest === "undefined") {
3 | window.CrystalQuest = {};
4 | }
5 | var MovingObject = window.CrystalQuest.MovingObject;
6 |
7 | CrystalQuest.LaserAlien = function (options) {
8 | MovingObject.call(this, options);
9 | this.radius = 12;
10 | this.img = 'img/laser_alien.png';
11 | };
12 |
13 | window.CrystalQuest.Util.inherits(CrystalQuest.LaserAlien, MovingObject);
14 |
15 | CrystalQuest.LaserAlien.prototype.startIntervals = function() {
16 | var that = this;
17 | this.dirInterval = setInterval( function () {
18 | that.vel = window.CrystalQuest.Util.randomVec(3);
19 | if (that.wave.gameOver) {
20 | that.clearIntervals();
21 | }
22 | }, 500);
23 | // this.laserInterval = ...to write...
24 | };
25 | })();
26 |
--------------------------------------------------------------------------------
/lib/movingObject.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | if (typeof CrystalQuest === "undefined") {
3 | window.CrystalQuest = {};
4 | }
5 | var Asteroid = window.CrystalQuest.Asteroid;
6 | var Portal = window.CrystalQuest.Portal;
7 | var BigCrystal = window.CrystalQuest.BigCrystal;
8 | var BasicAlien = window.CrystalQuest.BasicAlien;
9 |
10 | var MovingObject = window.CrystalQuest.MovingObject = function ( obj ) {
11 | this.pos = obj['pos'];
12 | this.vel = obj['vel'];
13 | this.radius = obj['radius'];
14 | this.color = obj['color'];
15 | this.wave = obj['wave'];
16 | };
17 |
18 | MovingObject.prototype.draw = function(ctx) {
19 | if (this instanceof CrystalQuest.BlobAlien) {
20 | var x = this.pos[0];
21 | var y = this.pos[1];
22 | var img = new Image();
23 | img.src = this.img;
24 | var count = Math.floor(this.wave.counter / 16);
25 | var xoff = (count % 4) * 100;
26 |
27 | ctx.drawImage(
28 | img,
29 | xoff,0,100,100,
30 | x,y,50, (this.radius * 3)
31 | );
32 | } else if (this instanceof CrystalQuest.BasicAlien) {
33 | var x = this.pos[0] - this.radius;
34 | var y = this.pos[1] - this.radius;
35 | var img = new Image();
36 | img.src = this.img;
37 | var count = Math.floor(this.wave.counter / 24);
38 | var xoff = (count % 2) * 100;
39 |
40 | ctx.drawImage(
41 | img,
42 | xoff,0,100,100,
43 | x,y,(this.radius * 2), (this.radius * 2)
44 | );
45 | } else if (this.img !== undefined) {
46 | var img = new Image();
47 | x = this.pos[0] - this.radius;
48 | y = this.pos[1] - this.radius;
49 | img.src = this.img;
50 | ctx.drawImage(img, x, y, this.radius * 2, this.radius * 2);
51 | } else {
52 | ctx.fillStyle = this.color;
53 | ctx.beginPath();
54 |
55 | ctx.arc(
56 | this.pos[0],
57 | this.pos[1],
58 | this.radius,
59 | 0,
60 | 2 * Math.PI,
61 | false
62 | );
63 |
64 | ctx.fill();
65 | }
66 | };
67 |
68 | MovingObject.prototype.isBounceable = function () {
69 | return true;
70 | };
71 |
72 | MovingObject.prototype.move = function() {
73 | var newPos = [this.pos[0] + this.vel[0], this.pos[1] + this.vel[1]];
74 | if ((newPos[0] - this.radius < 0) || (newPos[1] - this.radius < 0) || (newPos[0] + this.radius > 780) || (newPos[1] + this.radius > 500)) {
75 | if (this.isBounceable()) {
76 | this.wave.bounce(this);
77 | this.pos = [this.pos[0] + this.vel[0], this.pos[1] + this.vel[1]]
78 | } else if (this instanceof CrystalQuest.Ship) {
79 | this.pos = newPos
80 | } else {
81 | var collection = this instanceof CrystalQuest.AlienBullet ? this.wave.alienBullets : this.wave.bullets;
82 | this.wave.remove(collection, this);
83 | }
84 | } else {
85 | this.pos = newPos
86 | }
87 | };
88 |
89 | MovingObject.prototype.isCollidedWith = function(otherObject, pos, rad) {
90 | var coords = pos || this.pos;
91 | var radius = rad || this.radius;
92 | if (otherObject instanceof CrystalQuest.Portal) {
93 | var xRightPos = coords[0] - 780;
94 | var xLeftPos = coords[0] - 0;
95 | var yPos = coords[1] - (500 / 2);
96 | var rightDist = Math.pow((Math.pow(xRightPos, 2) + Math.pow(yPos, 2)), (1/2));
97 | var leftDist = Math.pow((Math.pow(xLeftPos, 2) + Math.pow(yPos, 2)), (1/2));
98 |
99 | if ((rightDist < radius + otherObject.width) || (leftDist < radius + otherObject.width)) {
100 | return true;
101 | }
102 | } else if ((otherObject instanceof CrystalQuest.Crystal) || (otherObject instanceof CrystalQuest.Points)){
103 | var xPos = coords[0] - otherObject.pos[0];
104 | var yPos = coords[1] - otherObject.pos[1];
105 | var dist = Math.pow((Math.pow(xPos, 2) + Math.pow(yPos, 2)), (1/2));
106 |
107 | if (dist < radius + (otherObject.width / 2)) {
108 | return true;
109 | }
110 | } else if (otherObject && otherObject.width !== undefined) {
111 | var xPos = coords[0] - otherObject.pos[0] - (otherObject.width / 2);
112 | var yPos = coords[1] - otherObject.pos[1] - (otherObject.width / 2);
113 | var dist = Math.pow((Math.pow(xPos, 2) + Math.pow(yPos, 2)), (1/2));
114 |
115 | if (dist < radius + (otherObject.width / 2)) {
116 | return true;
117 | }
118 | } else if (otherObject) {
119 | var xPos = coords[0] - otherObject.pos[0];
120 | var yPos = coords[1] - otherObject.pos[1];
121 | var dist = Math.pow((Math.pow(xPos, 2) + Math.pow(yPos, 2)), (1/2));
122 |
123 | if (dist < radius + otherObject.radius) {
124 | return true;
125 | }
126 | }
127 | };
128 |
129 | MovingObject.prototype.clearIntervals = function() {
130 | clearInterval(this.dirInterval);
131 | this.dirInterval = null;
132 | clearInterval(this.shootInterval);
133 | this.shootInterval = null;
134 | clearInterval(this.hashInterval);
135 | this.hashInterval = null;
136 | };
137 |
138 | })();
139 |
--------------------------------------------------------------------------------
/lib/points.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | if (typeof CrystalQuest === "undefined") {
3 | window.CrystalQuest = {};
4 | }
5 |
6 | var Points = CrystalQuest.Points = function (options) {
7 | this.pos = options.pos;
8 | this.wave = options.wave;
9 | this.num = Math.floor(Math.random() * (8000) + 1000);
10 | this.width = 20;
11 | };
12 |
13 | Points.prototype.draw = function(ctx) {
14 | ctx.fillStyle = "#FFFFFF";
15 | ctx.font = "12pt Arial";
16 | ctx.fillText(this.num, this.pos[0], this.pos[1]);
17 | };
18 |
19 | })();
20 |
--------------------------------------------------------------------------------
/lib/portal.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | if (typeof CrystalQuest === "undefined") {
3 | window.CrystalQuest = {};
4 | }
5 |
6 | var Portal = CrystalQuest.Portal = function (options) {
7 | this.color = "#BF3EFF";
8 | this.wave = options.wave;
9 | this.height = 60;
10 | this.width = 30;
11 | };
12 |
13 | Portal.RIGHT_POS = [780, ((500 / 2) - 30)];
14 | Portal.LEFT_POS = [0, ((500 / 2) - 30)];
15 |
16 | Portal.prototype.draw = function(ctx) {
17 | ctx.fillStyle = this.color;
18 | ctx.fillRect(Portal.LEFT_POS[0], Portal.LEFT_POS[1], 30, 60);
19 | ctx.fill();
20 |
21 | ctx.fillStyle = this.color;
22 | ctx.fillRect(Portal.RIGHT_POS[0], Portal.RIGHT_POS[1], -30, 60);
23 | ctx.fill();
24 | };
25 |
26 |
27 |
28 | })();
29 |
--------------------------------------------------------------------------------
/lib/ship.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | if (typeof CrystalQuest === "undefined") {
3 | window.CrystalQuest = {};
4 | }
5 | var MovingObject = window.CrystalQuest.MovingObject;
6 | var Wave = window.CrystalQuest.Wave;
7 | var Bullet = window.CrystalQuest.Bullet;
8 |
9 | CrystalQuest.Ship = function (options) {
10 | MovingObject.call(this, options);
11 | this.color = options.color;
12 | this.radius = 12;
13 | this.vel = [0,0];
14 | };
15 |
16 | window.CrystalQuest.Util.inherits(CrystalQuest.Ship, MovingObject);
17 |
18 | CrystalQuest.Ship.prototype.relocate = function() {
19 | this.pos = window.CrystalQuest.Wave.randomPosition(12);
20 | this.vel = [0,0];
21 | };
22 |
23 | CrystalQuest.Ship.prototype.power = function(impulse) {
24 | this.vel = impulse;
25 | };
26 |
27 | CrystalQuest.Ship.prototype.fireBullet = function() {
28 | var norm = CrystalQuest.Util.norm(this.vel);
29 |
30 | if (norm == 0) {
31 | return;
32 | }
33 |
34 | var relVel = CrystalQuest.Util.scale(
35 | CrystalQuest.Util.dir(this.vel),
36 | CrystalQuest.Bullet.SPEED
37 | );
38 |
39 | var bulletVel = [
40 | relVel[0] + this.vel[0], relVel[1] + this.vel[1]
41 | ];
42 |
43 | var bullet = new window.CrystalQuest.Bullet({
44 | pos: this.pos,
45 | vel: bulletVel,
46 | wave: this.wave
47 | });
48 | this.wave.bullets.push(bullet);
49 | this.wave.game.playSound('bullet');
50 | };
51 |
52 | CrystalQuest.Ship.prototype.isBounceable = function () {
53 | leftSide = (780 / 2) - 30
54 | rightSide = (780 / 2) + 30
55 | if ((this.pos[0] <= rightSide) && (this.pos[0] >= leftSide) && (this.pos[1] > 0) && (this.wave.crystals.length === 0)) {
56 | return false;
57 | } else {
58 | return true;
59 | }
60 | };
61 |
62 | })();
63 |
--------------------------------------------------------------------------------
/lib/shooterAlien.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | if (typeof CrystalQuest === "undefined") {
3 | window.CrystalQuest = {};
4 | }
5 | var MovingObject = window.CrystalQuest.MovingObject;
6 |
7 | var ShooterAlien = CrystalQuest.ShooterAlien = function (options) {
8 | MovingObject.call(this, options);
9 | this.radius = 12;
10 | this.img = 'img/shooter_alien.png';
11 | this.bulletImg = 'img/alien_bullet1.png';
12 | };
13 |
14 | window.CrystalQuest.Util.inherits(ShooterAlien, MovingObject);
15 |
16 | ShooterAlien.prototype.startIntervals = function() {
17 | var that = this;
18 | this.dirInterval = setInterval( function () {
19 | that.vel = window.CrystalQuest.Util.randomVec(3);
20 |
21 | if (that.wave.gameOver) {
22 | that.clearIntervals();
23 | }
24 | }, 500);
25 |
26 | this.shootInterval = setInterval( function () {
27 | var bulletVel = window.CrystalQuest.Util.randomVec(2);
28 | var bullet = new window.CrystalQuest.AlienBullet({
29 | pos: that.pos,
30 | vel: bulletVel,
31 | wave: that.wave,
32 | img: that.bulletImg
33 | });
34 |
35 | if (that.pos[0] > 12 && that.pos[0] < (780 - 12)) {
36 | that.wave.alienBullets.push(bullet);
37 | }
38 |
39 | if (that.wave.gameOver || that.wave.aliens.indexOf(that) < 0) {
40 | that.clearIntervals();
41 | }
42 | }, 500);
43 | };
44 |
45 | })();
46 |
--------------------------------------------------------------------------------
/lib/utils.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | if (typeof CrystalQuest === "undefined") {
3 | window.CrystalQuest = {};
4 | }
5 | var Util = window.CrystalQuest.Util = {};
6 |
7 | Util.inherits = function (ChildClass, ParentClass) {
8 | function Surrogate () {}
9 | Surrogate.prototype = ParentClass.prototype;
10 | ChildClass.prototype = new Surrogate();
11 | };
12 |
13 | Util.randomVec = function (length) {
14 | var deg = 2 * Math.PI * Math.random();
15 | return Util.scale([Math.sin(deg), Math.cos(deg)], length);
16 | };
17 |
18 | Util.scale = function (vec, m) {
19 | return [vec[0] * m, vec[1] * m];
20 | };
21 |
22 | Util.dir = function (vec) {
23 | var norm = Util.norm(vec);
24 | return Util.scale(vec, 1 / norm);
25 | };
26 |
27 | Util.dist = function (pos1, pos2) {
28 | return Math.sqrt(
29 | Math.pow(pos1[0] - pos2[0], 2) + Math.pow(pos1[1] - pos2[1], 2)
30 | );
31 | };
32 |
33 | Util.norm = function (vec) {
34 | return Util.dist([0, 0], vec);
35 | };
36 |
37 | })();
38 |
--------------------------------------------------------------------------------
/lib/wave.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | if (typeof CrystalQuest === "undefined") {
3 | window.CrystalQuest = {};
4 | }
5 | var Alien = window.CrystalQuest.Alien;
6 | var Bullet = window.CrystalQuest.Bullet;
7 | var Ship = window.CrystalQuest.Ship;
8 | var Asteroid = window.CrystalQuest.Asteroid;
9 | var Portal = window.CrystalQuest.Portal;
10 | var Bomb = window.CrystalQuest.Bomb;
11 | var CrystalPoints = window.CrystalQuest.CrystalPoints;
12 |
13 | var Wave = window.CrystalQuest.Wave = function (xDim, yDim, game, options) {
14 | this.game = game
15 | this.X_DIM = xDim;
16 | this.Y_DIM = yDim;
17 | this.NUM_BASIC_ALIENS = options.numBasicAliens || 0;
18 | this.NUM_SHOOTER_ALIENS = options.numShooterAliens || 0;
19 | this.NUM_BLOB_ALIENS = options.numBlobAliens || 0;
20 | this.NUM_COMPUTER_ALIENS = options.numComputerAliens || 0;
21 | this.NUM_CSHOOTER_ALIENS = options.numCShooterAliens || 0;
22 | this.NUM_XSHOOTER_ALIENS = options.numXShooterAliens || 0;
23 | this.NUM_LASER_ALIENS = options.numLaserAliens || 0;
24 | this.NUM_FOUR_LEG_ALIENS = options.numFourLegsAliens || 0;
25 | this.NUM_ASTEROIDS = options.numAsteroids;
26 | this.NUM_CRYSTALS = options.numCrystals;
27 | this.NUM_BIG_CRYSTALS = options.numBigCrystals;
28 | this.NUM_BOMBS = options.numBombs;
29 | this.NUM_POINTS = options.numPoints;
30 |
31 | // Need to explicitly unbind and bind the enter key each wave or else
32 | // flash bomb will get called multiple times on one keypress
33 | $(document).unbind('keydown').keydown(function (event) {
34 | event.preventDefault();
35 | if (event.keyCode === 16) {
36 | this.flashBomb();
37 | }
38 |
39 | if (event.keyCode === 27) {
40 | this.isQuit = true;
41 | }
42 | }.bind(this))
43 |
44 | this.bigCrystalPoints = [];
45 | this.hiddenAliens = [];
46 | this.aliens = [];
47 | this.ship = new window.CrystalQuest.Ship({ pos: Wave.randomPosition(12), wave: this, color: "#FFFFFF" });
48 | this.bullets = [];
49 | this.alienBullets = [];
50 | this.asteroids = this.addAsteroids();
51 | this.gate = new window.CrystalQuest.Gate({ wave: this });
52 | this.portals = new window.CrystalQuest.Portal({ wave: this });
53 | this.crystals = this.addCrystals();
54 | this.bigCrystals = [];
55 | this.bombs = this.addBombs();
56 | this.points = this.addPoints();
57 | this.flash = false;
58 | this.gameOver = false;
59 | this.crystalPoints = false;
60 | this.counter = 0;
61 | this.isLost = false;
62 | this.isQuit = false;
63 | this.clock = new window.CrystalQuest.Clock();
64 | this.clock.run()
65 | this.addAliens();
66 | this.addBigCrystals();
67 | };
68 |
69 | Wave.randomPosition = function (radius) {
70 | var randX = Math.floor(Math.random() * (780 - radius * 2)) + radius;
71 | var randY = Math.floor(Math.random() * (500 - radius * 2)) + radius;
72 | return [randX, randY];
73 | };
74 |
75 | Wave.randomAsteroidPos = function (width) {
76 | do {
77 | var randX = Math.floor(Math.random() * (780 - width)) + (width/2);
78 | var randY = Math.floor(Math.random() * (500 - width)) + (width/2);
79 | } while (
80 | ((randX - 15 < 30 || randX + 15 > 750) && (randY > 220 && randY < 280))
81 | || (randY + 20 > 500)
82 | )
83 | return [randX, randY];
84 | };
85 |
86 | Wave.prototype.checkAsteroids = function(coords) {
87 | for (var i = 0; i < this.asteroids.length; i++) {
88 | var asteroidPos = this.asteroids[i].pos;
89 | if (coords[0] > asteroidPos[0] && coords[0] < asteroidPos[0] + 20 && coords[1] > asteroidPos[1] && coords[1] < asteroidPos[1] + 20) {
90 | return true;
91 | }
92 | }
93 |
94 | return false;
95 | };
96 |
97 | Wave.prototype.randomSafePos = function(width) {
98 | var coords = Wave.randomAsteroidPos(width);
99 | do {
100 | var coords = Wave.randomAsteroidPos(width);
101 | } while (
102 | this.checkAsteroids(coords) === true
103 | )
104 | return coords;
105 | };
106 |
107 | Wave.prototype.addPoints = function () {
108 | var result = [];
109 | for (var i = 0; i < this.NUM_POINTS; i++) {
110 | var points = new window.CrystalQuest.Points({
111 | pos: Wave.randomAsteroidPos(50),
112 | wave: this
113 | });
114 | result.push(points)
115 | }
116 | return result;
117 | };
118 |
119 | Wave.prototype.addBombs = function () {
120 | var result = [];
121 | for(var i = 0; i < this.NUM_BOMBS; i++) {
122 | var bomb = new window.CrystalQuest.Bomb({
123 | pos: Wave.randomAsteroidPos(15),
124 | wave: this
125 | });
126 | result.push(bomb);
127 | }
128 | return result;
129 | };
130 |
131 | Wave.prototype.addCrystals = function () {
132 | var result = [];
133 | for(var i = 0; i < this.NUM_CRYSTALS; i++) {
134 | var crystal = new window.CrystalQuest.Crystal({
135 | pos: this.randomSafePos(15),
136 | wave: this
137 | });
138 | result.push(crystal);
139 | }
140 | return result;
141 | };
142 |
143 | Wave.prototype.addAsteroids = function () {
144 | var result = [];
145 | for(var i = 0; i < this.NUM_ASTEROIDS; i++) {
146 | var asteroid = new window.CrystalQuest.Asteroid({
147 | pos: Wave.randomAsteroidPos(10),
148 | wave: this
149 | });
150 | result.push(asteroid);
151 | }
152 | return result;
153 | };
154 |
155 | Wave.prototype.alienStop = function() {
156 | clearTimeout(this.alienInterval);
157 | this.alienInterval = null;
158 | };
159 |
160 | Wave.prototype.bcStop = function() {
161 | clearTimeout(this.bcInterval);
162 | this.bcInterval = null;
163 | };
164 |
165 | Wave.prototype.addAliens = function () {
166 | for(var i = 0; i < this.NUM_BASIC_ALIENS; i++) {
167 | if (i < (this.NUM_BASIC_ALIENS / 2) ) {
168 | var alien = new window.CrystalQuest.BasicAlien({
169 | pos: [this.X_DIM - 12, (this.Y_DIM / 2)],
170 | wave: this,
171 | vel: window.CrystalQuest.Util.randomVec(5)
172 | });
173 | } else {
174 | var alien = new window.CrystalQuest.BasicAlien({
175 | pos: [12, (this.Y_DIM / 2)],
176 | wave: this,
177 | vel: window.CrystalQuest.Util.randomVec(5)
178 | });
179 | }
180 | this.hiddenAliens.push(alien);
181 | }
182 | for(var i = 0; i < this.NUM_SHOOTER_ALIENS; i++) {
183 | if (i < (this.NUM_SHOOTER_ALIENS / 2) ) {
184 | var alien = new window.CrystalQuest.ShooterAlien({
185 | pos: [this.X_DIM - 12, (this.Y_DIM / 2)],
186 | wave: this,
187 | vel: window.CrystalQuest.Util.randomVec(5)
188 | });
189 | } else {
190 | var alien = new window.CrystalQuest.ShooterAlien({
191 | pos: [12, (this.Y_DIM / 2)],
192 | wave: this,
193 | vel: window.CrystalQuest.Util.randomVec(5)
194 | });
195 | }
196 | this.hiddenAliens.push(alien);
197 | }
198 | for(var i = 0; i < this.NUM_BLOB_ALIENS; i++) {
199 | if (i < (this.NUM_BLOB_ALIENS / 2) ) {
200 | var alien = new window.CrystalQuest.BlobAlien({
201 | pos: [this.X_DIM - 20, (this.Y_DIM / 2)],
202 | wave: this,
203 | vel: window.CrystalQuest.Util.randomVec(5)
204 | });
205 | } else {
206 | var alien = new window.CrystalQuest.BlobAlien({
207 | pos: [20, (this.Y_DIM / 2)],
208 | wave: this,
209 | vel: window.CrystalQuest.Util.randomVec(5)
210 | });
211 | }
212 | this.hiddenAliens.push(alien);
213 | }
214 | for(var i = 0; i < this.NUM_COMPUTER_ALIENS; i++) {
215 | if (i < (this.NUM_COMPUTER_ALIENS / 2) ) {
216 | var alien = new window.CrystalQuest.ComputerAlien({
217 | pos: [this.X_DIM - 12, (this.Y_DIM / 2)],
218 | wave: this,
219 | vel: window.CrystalQuest.Util.randomVec(5)
220 | });
221 | } else {
222 | var alien = new window.CrystalQuest.ComputerAlien({
223 | pos: [12, (this.Y_DIM / 2)],
224 | wave: this,
225 | vel: window.CrystalQuest.Util.randomVec(5)
226 | });
227 | }
228 | this.hiddenAliens.push(alien);
229 | }
230 | for(var i = 0; i < this.NUM_CSHOOTER_ALIENS; i++) {
231 | if (i < (this.NUM_CSHOOTER_ALIENS / 2) ) {
232 | var alien = new window.CrystalQuest.CSAlien({
233 | pos: [this.X_DIM - 12, (this.Y_DIM / 2)],
234 | wave: this,
235 | vel: window.CrystalQuest.Util.randomVec(5)
236 | });
237 | } else {
238 | var alien = new window.CrystalQuest.CSAlien({
239 | pos: [12, (this.Y_DIM / 2)],
240 | wave: this,
241 | vel: window.CrystalQuest.Util.randomVec(5)
242 | });
243 | }
244 | this.hiddenAliens.push(alien);
245 | }
246 | for(var i = 0; i < this.NUM_XSHOOTER_ALIENS; i++) {
247 | if (i < (this.NUM_XSHOOTER_ALIENS / 2) ) {
248 | var alien = new window.CrystalQuest.XSAlien({
249 | pos: [this.X_DIM - 12, (this.Y_DIM / 2)],
250 | wave: this,
251 | vel: window.CrystalQuest.Util.randomVec(5)
252 | });
253 | } else {
254 | var alien = new window.CrystalQuest.XSAlien({
255 | pos: [12, (this.Y_DIM / 2)],
256 | wave: this,
257 | vel: window.CrystalQuest.Util.randomVec(5)
258 | });
259 | }
260 | this.hiddenAliens.push(alien);
261 | }
262 | for(var i = 0; i < this.NUM_FOUR_LEG_ALIENS; i++) {
263 | if (i < (this.NUM_FOUR_LEG_ALIENS / 2) ) {
264 | var alien = new window.CrystalQuest.FourLegAlien({
265 | pos: [this.X_DIM - 12, (this.Y_DIM / 2)],
266 | wave: this,
267 | vel: window.CrystalQuest.Util.randomVec(5)
268 | });
269 | } else {
270 | var alien = new window.CrystalQuest.FourLegAlien({
271 | pos: [12, (this.Y_DIM / 2)],
272 | wave: this,
273 | vel: window.CrystalQuest.Util.randomVec(5)
274 | });
275 | }
276 | this.hiddenAliens.push(alien);
277 | }
278 | var that = this;
279 | this.hiddenAliens.forEach( function (alien) {
280 | var wait = (Math.random() * 20000);
281 | if (!this.gameOver) {
282 | this.alienInterval = setTimeout( function () {
283 | if (!that.gameOver) {
284 | that.aliens.push(alien);
285 | alien.startIntervals();
286 | that.alienStop();
287 | }
288 | }, wait);
289 | }
290 | });
291 | };
292 |
293 | Wave.prototype.addBigCrystals = function () {
294 | var result = [];
295 | for (var i = 0; i < this.NUM_BIG_CRYSTALS; i++) {
296 | var bigCrystal = new window.CrystalQuest.BigCrystal({
297 | pos: [this.X_DIM - 20, (this.Y_DIM / 2)],
298 | wave: this,
299 | vel: [1, -1]
300 | })
301 | result.push(bigCrystal);
302 | }
303 | var that = this;
304 | result.forEach( function (bigCrystal) {
305 | var wait = (Math.random() * 18000)
306 | that.bcInterval = setTimeout( function () {
307 | that.bigCrystals.push(bigCrystal)
308 | that.bcStop();
309 | }, wait);
310 | })
311 | };
312 |
313 | Wave.prototype.allObjects = function() {
314 | var movingObjects = this.movingObjects();
315 | return ([this.gate]
316 | .concat(this.asteroids)
317 | .concat(this.bombs)
318 | .concat(this.portals)
319 | .concat(this.crystals)
320 | .concat(this.points)
321 | .concat(this.bigCrystalPoints)
322 | .concat(movingObjects)
323 | );
324 | };
325 |
326 | Wave.prototype.movingObjects = function() {
327 | return ([this.ship]
328 | .concat(this.aliens)
329 | .concat(this.bullets)
330 | .concat(this.bigCrystals)
331 | .concat(this.alienBullets)
332 | );
333 | };
334 |
335 | Wave.prototype.draw = function(ctx) {
336 | if (this.flash) {
337 | ctx.fillStyle = "#FF0000";
338 | ctx.fillRect(0, 0, this.X_DIM, this.Y_DIM);
339 | var _this = this;
340 | setTimeout( function(e) {
341 | _this.flash = false;
342 | }, 90);
343 | } else {
344 | ctx.fillStyle = "#000000";
345 | ctx.fillRect(0, 0, this.X_DIM, this.Y_DIM);
346 | }
347 | var objects = this.allObjects();
348 | objects.forEach(function (obj) {
349 | obj.draw(ctx);
350 | });
351 | };
352 |
353 | Wave.prototype.moveObjects = function () {
354 | var objects = this.movingObjects();
355 | objects.forEach(function (obj) {
356 | obj.move();
357 | });
358 | };
359 |
360 | Wave.prototype.bounce = function (object) {
361 | xVel = object.vel[0];
362 | yVel = object.vel[1];
363 | var dirs = [-1, 1];
364 | var idx = Math.floor(Math.random() * 2);
365 |
366 | if (xVel < 0 && yVel < 0) {
367 | object.vel = [-xVel, dirs[idx] * yVel]
368 | } else if (xVel < 0 && yVel > 0) {
369 | object.vel = [dirs[idx] * xVel, -yVel]
370 | } else if (xVel > 0 && yVel > 0) {
371 | object.vel = [-xVel, dirs[idx] * yVel]
372 | } else if (xVel > 0 && yVel < 0) {
373 | object.vel = [dirs[idx] * xVel, -yVel]
374 | } else if (xVel === 0 && yVel !== 0) {
375 | object.vel = [xVel, -yVel]
376 | } else if (yVel === 0 && xVel !== 0) {
377 | object.vel = [-xVel, yVel]
378 | }
379 | };
380 |
381 | Wave.prototype.remove = function (collection, object) {
382 | if (object instanceof CrystalQuest.ShooterAlien) {
383 | object.clearIntervals();
384 | }
385 |
386 | if (collection && collection.indexOf(object) > -1) {
387 | collection.splice(collection.indexOf(object), 1);
388 | } else {
389 | object = [];
390 | }
391 | };
392 |
393 | Wave.prototype.loseALife = function (relocate) {
394 | if ($('#lives').find('img').length === 0) {
395 | this.isLost = true;
396 | } else if ($('#life-count').text() === "") {
397 | $('#lives').find('img').last().remove();
398 | this.flashBomb(true);
399 | } else if ($('#life-count').text() === "5"){
400 | $('#life-count').html("")
401 | $('#lives').prepend(' ');
402 | // this.ship.relocate();
403 | this.flashBomb(true);
404 | } else {
405 | var count = parseInt($('#life-count').text()) - 1
406 | $('#life-count').html(count);
407 | // this.ship.relocate();
408 | this.flashBomb(true);
409 | }
410 |
411 | if (relocate) {
412 | this.ship.relocate();
413 | }
414 |
415 | this.game.playSound('wall');
416 | };
417 |
418 | Wave.prototype.flashBomb = function (loseALife) {
419 | if ($('#bombs').find('img').length > 0 || loseALife) {
420 | if (!loseALife) {
421 | if ($('#bomb-count').text() === "") {
422 | $('#bombs').children().first().remove();
423 | } else if ($('#bomb-count').text() === "5") {
424 | $('#bomb-count').html("");
425 | $('#bombs').prepend(' ');
426 | } else {
427 | var count = parseInt($('#bomb-count').text()) - 1;
428 | $('#bomb-count').html(count);
429 | }
430 | }
431 | this.aliens.forEach( function (alien) {
432 | this.remove(this.aliens, alien);
433 | }.bind(this))
434 |
435 | this.aliens = [];
436 | this.alienBullets = [];
437 | this.flash = true;
438 | }
439 | };
440 |
441 | Wave.prototype.checkCollisions = function () {
442 | for (var i = 0; i < this.asteroids.length; i++) {
443 | if (this.ship.isCollidedWith(this.asteroids[i])) {
444 | this.loseALife(true);
445 | }
446 | }
447 |
448 | if (this.ship.isCollidedWith(this.portals)) {
449 | this.loseALife(true);
450 | }
451 |
452 | for (var i = 0; i < this.alienBullets.length; i++) {
453 | if (this.ship.isCollidedWith(this.alienBullets[i])) {
454 | this.loseALife();
455 | }
456 | }
457 |
458 | for (var i = 0; i < this.crystals.length; i++) {
459 | if (this.ship.isCollidedWith(this.crystals[i])) {
460 | this.game.playSound('crystal');
461 | var score = parseInt($('#score').text()) + 200;
462 | $('#score').html(score);
463 | this.remove(this.crystals, this.crystals[i]);
464 | }
465 | }
466 |
467 | for (var i = 0; i < this.bombs.length; i++) {
468 | if (this.ship.isCollidedWith(this.bombs[i])) {
469 | if (($('#bombs').find('img').length < 4) && ($('#bomb-count').text() === "")) {
470 | $('#bombs').prepend(' ')
471 | } else if ($('#bomb-count').text() === "") {
472 | $('#bombs').find('img').last().remove();
473 | $('#bomb-count').html(5)
474 | } else {
475 | var count = parseInt($('#bomb-count').text()) + 1
476 | $('#bomb-count').html(count)
477 | }
478 | this.remove(this.bombs, this.bombs[i])
479 | }
480 | }
481 |
482 | for (var i = 0; i < this.aliens.length; i++) {
483 | for (var j = 0; j < this.bullets.length; j++) {
484 | if ((this.aliens[i] !== undefined) && this.aliens[i].isCollidedWith(this.bullets[j])) {
485 | this.remove(this.aliens, this.aliens[i]);
486 | this.remove(this.bullets, this.bullets[j]);
487 | }
488 | }
489 | if (this.ship.isCollidedWith(this.aliens[i])) {
490 | this.loseALife();
491 | }
492 | }
493 |
494 | for (var i = 0; i < this.bigCrystals.length; i++) {
495 | if (this.ship.isCollidedWith(this.bigCrystals[i])) {
496 | this.game.playSound('success');
497 |
498 | if (($('#lives').find('img').length < 4) && ($('#life-count').text() === "")) {
499 | $('#lives').prepend(' ');
500 | } else if ($('#life-count').text() === "") {
501 | $('#lives').find('img').last().remove();
502 | $('#life-count').html(5);
503 | } else {
504 | var count = parseInt($('#life-count').text()) + 1
505 | $('#life-count').html(count);
506 | }
507 | var points = new window.CrystalQuest.CrystalPoints({
508 | pos: this.bigCrystals[i].pos,
509 | wave: this,
510 | num: this.bigCrystals[i].num
511 | });
512 | this.bigCrystalPoints.push(points);
513 | var score = parseInt($('#score').text()) + this.bigCrystals[i].num;
514 | $('#score').html(score);
515 | this.remove(this.bigCrystals, this.bigCrystals[i]);
516 | var that = this;
517 | setTimeout(function () {
518 | that.remove(that.bigCrystalPoints, points);
519 | }, 3000)
520 | }
521 | }
522 |
523 | for (var i = 0; i < this.points.length; i++) {
524 | if (this.ship.isCollidedWith(this.points[i])) {
525 | this.game.playSound('crystal');
526 | var score = parseInt($('#score').text()) + this.points[i].num;
527 | $('#score').html(score);
528 | this.remove(this.points, this.points[i]);
529 | }
530 | }
531 | };
532 |
533 | Wave.prototype.isWon = function () {
534 | if ((this.crystals.length === 0) && (this.ship.pos[1] > this.Y_DIM)) {
535 | return true;
536 | }
537 | };
538 |
539 | Wave.prototype.isOutOfBounds = function (pos) {
540 | return (pos[0] < 0) || (pos[1] < 0)
541 | || (pos[0] > this.X_DIM) || (pos[1] > this.Y_DIM);
542 | };
543 |
544 | Wave.prototype.step = function() {
545 | this.moveObjects();
546 | this.checkCollisions();
547 | this.counter++;
548 |
549 | if (this.isWon()) {
550 | this.endWave();
551 | this.game.playSound('levelSuccess');
552 | return "won";
553 | } else if (this.isLost) {
554 | this.endWave();
555 | this.game.playSound('tristeza');
556 | return "lost";
557 | } else if (this.isQuit) {
558 | this.endWave();
559 | return "quit";
560 | }
561 | };
562 |
563 | Wave.prototype.endWave = function() {
564 | this.clock.stop();
565 | this.gameOver = true;
566 | this.alienStop();
567 | this.bcStop();
568 |
569 | this.aliens.forEach( function (alien) {
570 | this.remove(this.aliens, alien);
571 | }.bind(this));
572 |
573 | this.hiddenAliens.forEach( function (alien) {
574 | this.remove(this.hiddenAliens, alien);
575 | }.bind(this));
576 |
577 | this.aliens = [];
578 | this.hiddenAliens = [];
579 | $(document).unbind('keydown').bind(this);
580 | };
581 |
582 | })();
583 |
--------------------------------------------------------------------------------
/sounds/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/haleymt/CrystalQuest/8bd29df2ca65d264fecf37aabd32659ba0a3c153/sounds/.DS_Store
--------------------------------------------------------------------------------
/sounds/crystal.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/haleymt/CrystalQuest/8bd29df2ca65d264fecf37aabd32659ba0a3c153/sounds/crystal.wav
--------------------------------------------------------------------------------
/sounds/laser.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/haleymt/CrystalQuest/8bd29df2ca65d264fecf37aabd32659ba0a3c153/sounds/laser.wav
--------------------------------------------------------------------------------
/sounds/levelSuccess.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/haleymt/CrystalQuest/8bd29df2ca65d264fecf37aabd32659ba0a3c153/sounds/levelSuccess.wav
--------------------------------------------------------------------------------
/sounds/reverbLaser.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/haleymt/CrystalQuest/8bd29df2ca65d264fecf37aabd32659ba0a3c153/sounds/reverbLaser.wav
--------------------------------------------------------------------------------
/sounds/shortLaser.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/haleymt/CrystalQuest/8bd29df2ca65d264fecf37aabd32659ba0a3c153/sounds/shortLaser.wav
--------------------------------------------------------------------------------
/sounds/success.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/haleymt/CrystalQuest/8bd29df2ca65d264fecf37aabd32659ba0a3c153/sounds/success.mp3
--------------------------------------------------------------------------------
/sounds/tristeza.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/haleymt/CrystalQuest/8bd29df2ca65d264fecf37aabd32659ba0a3c153/sounds/tristeza.wav
--------------------------------------------------------------------------------