diff --git a/Invaders/index.html b/Invaders/index.html
new file mode 100644
index 0000000..47f5b6a
--- /dev/null
+++ b/Invaders/index.html
@@ -0,0 +1,21 @@
+
+
+
+
+ Nathan Woodburn
+
+
+
+
+
+
+
+Space Invadors destroyed this page! Take revenge on them!
+
Use Space to shoot and ← → to move!
+
+
+
+
+
+
+
diff --git a/Invaders/script.js b/Invaders/script.js
new file mode 100644
index 0000000..13a7415
--- /dev/null
+++ b/Invaders/script.js
@@ -0,0 +1,446 @@
+;(function() {
+ "use strict";
+
+ // General
+ var canvas,
+ screen,
+ gameSize,
+ game;
+
+ // Assets
+ var invaderCanvas,
+ invaderMultiplier,
+ invaderSize = 20,
+ initialOffsetInvader,
+ invaderAttackRate,
+ invaderSpeed,
+ invaderSpawnDelay = 250;
+
+ // Counter
+ var i = 0,
+ kills = 0,
+ spawnDelayCounter = invaderSpawnDelay;
+
+ var invaderDownTimer;
+
+ // Text
+ var blocks = [
+ [3, 4, 8, 9, 10, 15, 16],
+ [2, 4, 7, 11, 14, 16],
+ [1, 4, 7, 11, 13, 16],
+ [1, 2, 3, 4, 5, 7, 11, 13, 14, 15, 16, 17],
+ [4, 7, 11, 16],
+ [4, 8, 9, 10, 16]
+ ];
+
+ // Game Controller
+ // ---------------
+ var Game = function() {
+
+ this.level = -1;
+ this.lost = false;
+
+ this.player = new Player();
+ this.invaders = [];
+ this.invaderShots = [];
+
+ if (invaderDownTimer === undefined) {
+ invaderDownTimer = setInterval(function() {
+ for (i = 0; i < game.invaders.length; i++) game.invaders[i].move();
+ }, 1000 - (this.level * 1.8));
+
+ };
+ }
+
+ Game.prototype = {
+ update: function() {
+
+ // Next level
+ if (game.invaders.length === 0) {
+
+ spawnDelayCounter += 1;
+ if (spawnDelayCounter < invaderSpawnDelay) return;
+
+ this.level += 1;
+
+ invaderAttackRate -= 0.002;
+ invaderSpeed += 10;
+
+ game.invaders = createInvaders();
+
+ spawnDelayCounter = 0;
+ }
+
+ if (!this.lost) {
+
+ // Collision
+ game.player.projectile.forEach(function(projectile) {
+ game.invaders.forEach(function(invader) {
+ if (collides(projectile, invader)) {
+ invader.destroy();
+ projectile.active = false;
+ }
+ });
+ });
+
+ this.invaderShots.forEach(function(invaderShots) {
+ if (collides(invaderShots, game.player)) {
+ game.player.destroy();
+ }
+ });
+
+ for (i = 0; i < game.invaders.length; i++) game.invaders[i].update();
+
+ }
+
+ // Don't stop player & projectiles.. they look nice
+ game.player.update();
+ for (i = 0; i < game.invaderShots.length; i++) game.invaderShots[i].update();
+
+ this.invaders = game.invaders.filter(function(invader) {
+ return invader.active;
+ });
+
+ },
+
+ draw: function() {
+
+ if (this.lost) {
+ screen.fillStyle = "rgba(0, 0, 0, 0.03)";
+ screen.fillRect(0, 0, gameSize.width, gameSize.height);
+
+ screen.font = "55px Lucida Console";
+ screen.textAlign = "center";
+ screen.fillStyle = "white";
+ screen.fillText("You lost", gameSize.width / 2, gameSize.height / 2);
+ screen.font = "20px Lucida Console";
+ screen.fillText("Points: " + kills, gameSize.width / 2, gameSize.height / 2 + 30);
+
+ } else {
+ screen.clearRect(0, 0, gameSize.width, gameSize.height);
+
+ screen.font = "10px Lucida Console";
+ screen.textAlign = "right";
+ screen.fillText("Points: " + kills, gameSize.width, gameSize.height - 12);
+ }
+
+ screen.beginPath();
+
+ var i;
+ this.player.draw();
+ if (!this.lost)
+ for (i = 0; i < this.invaders.length; i++) this.invaders[i].draw();
+ for (i = 0; i < this.invaderShots.length; i++) this.invaderShots[i].draw();
+
+ screen.fill();
+
+ },
+
+ invadersBelow: function(invader) {
+ return this.invaders.filter(function(b) {
+ return Math.abs(invader.coordinates.x - b.coordinates.x) === 0 &&
+ b.coordinates.y > invader.coordinates.y;
+ }).length > 0;
+ }
+
+ };
+
+ // Invaders
+ // --------
+ var Invader = function(coordinates) {
+ this.active = true;
+ this.coordinates = coordinates;
+ this.size = {
+ width: invaderSize,
+ height: invaderSize
+ };
+
+ this.patrolX = 0;
+ this.speedX = invaderSpeed;
+
+ };
+
+ Invader.prototype = {
+ update: function() {
+
+ if (Math.random() > invaderAttackRate && !game.invadersBelow(this)) {
+ var projectile = new Projectile({
+ x: this.coordinates.x + this.size.width / 2,
+ y: this.coordinates.y + this.size.height - 5
+ }, {
+ x: 0,
+ y: 2
+ });
+ game.invaderShots.push(projectile);
+ }
+
+ },
+ draw: function() {
+ if (this.active) screen.drawImage(invaderCanvas, this.coordinates.x, this.coordinates.y);
+
+ },
+ move: function() {
+ if (this.patrolX < 0 || this.patrolX > 100) {
+ this.speedX = -this.speedX;
+ this.patrolX += this.speedX;
+ this.coordinates.y += this.size.height;
+
+ if (this.coordinates.y + this.size.height * 2 > gameSize.height) game.lost = true;
+
+ } else {
+ this.coordinates.x += this.speedX;
+ this.patrolX += this.speedX;
+ }
+
+ },
+ destroy: function() {
+ this.active = false;
+ kills += 1;
+
+ }
+
+ };
+
+ // Player
+ // ------
+ var Player = function() {
+ this.active = true;
+ this.size = {
+ width: 16,
+ height: 8
+ };
+ this.shooterHeat = -3;
+ this.coordinates = {
+ x: gameSize.width / 2 - (this.size.width / 2) | 0,
+ y: gameSize.height - this.size.height * 2
+ };
+
+ this.projectile = [];
+ this.keyboarder = new KeyController();
+ };
+
+ Player.prototype = {
+ update: function() {
+
+ for (var i = 0; i < this.projectile.length; i++) this.projectile[i].update();
+
+ this.projectile = this.projectile.filter(function(projectile) {
+ return projectile.active;
+ });
+
+ if (!this.active) return;
+
+ if (this.keyboarder.isDown(this.keyboarder.KEYS.LEFT) && this.coordinates.x > 0) this.coordinates.x -= 2;
+ else if (this.keyboarder.isDown(this.keyboarder.KEYS.RIGHT) && this.coordinates.x < gameSize.width - this.size.width) this.coordinates.x += 2;
+
+ if (this.keyboarder.isDown(this.keyboarder.KEYS.Space)) {
+ this.shooterHeat += 1;
+ if (this.shooterHeat < 0) {
+ var projectile = new Projectile({
+ x: this.coordinates.x + this.size.width / 2 - 1,
+ y: this.coordinates.y - 1
+ }, {
+ x: 0,
+ y: -7
+ });
+ this.projectile.push(projectile);
+ } else if (this.shooterHeat > 12) this.shooterHeat = -3;
+ } else {
+ this.shooterHeat = -3;
+ }
+
+ },
+ draw: function() {
+ if (this.active) {
+ screen.rect(this.coordinates.x, this.coordinates.y, this.size.width, this.size.height);
+ screen.rect(this.coordinates.x - 2, this.coordinates.y + 2, 20, 6);
+ screen.rect(this.coordinates.x + 6, this.coordinates.y - 4, 4, 4);
+ }
+
+ for (var i = 0; i < this.projectile.length; i++) this.projectile[i].draw();
+
+ },
+ destroy: function() {
+ this.active = false;
+ game.lost = true;
+ }
+ };
+
+ // Projectile
+ // ------
+ var Projectile = function(coordinates, velocity) {
+ this.active = true;
+ this.coordinates = coordinates;
+ this.size = {
+ width: 3,
+ height: 3
+ };
+ this.velocity = velocity;
+ };
+
+ Projectile.prototype = {
+ update: function() {
+ this.coordinates.x += this.velocity.x;
+ this.coordinates.y += this.velocity.y;
+
+ if (this.coordinates.y > gameSize.height || this.coordinates.y < 0) this.active = false;
+
+ },
+ draw: function() {
+ if (this.active) screen.rect(this.coordinates.x, this.coordinates.y, this.size.width, this.size.height);
+
+ }
+ };
+
+ // Keyboard input tracking
+ // -----------------------
+ var KeyController = function() {
+ this.KEYS = {
+ LEFT: 37,
+ RIGHT: 39,
+ Space: 32
+ };
+ var keyCode = [37, 39, 32];
+ var keyState = {};
+
+ var counter;
+ window.addEventListener('keydown', function(e) {
+ for (counter = 0; counter < keyCode.length; counter++)
+ if (keyCode[counter] == e.keyCode) {
+ keyState[e.keyCode] = true;
+ e.preventDefault();
+ }
+
+ });
+
+ window.addEventListener('keyup', function(e) {
+ for (counter = 0; counter < keyCode.length; counter++)
+ if (keyCode[counter] == e.keyCode) {
+ keyState[e.keyCode] = false;
+ e.preventDefault();
+ }
+ });
+
+ this.isDown = function(keyCode) {
+ return keyState[keyCode] === true;
+ };
+
+ };
+
+ // Other functions
+ // ---------------
+ function collides(a, b) {
+ return a.coordinates.x < b.coordinates.x + b.size.width &&
+ a.coordinates.x + a.size.width > b.coordinates.x &&
+ a.coordinates.y < b.coordinates.y + b.size.height &&
+ a.coordinates.y + a.size.height > b.coordinates.y;
+ }
+
+ function getPixelRow(rowRaw) {
+ var textRow = [],
+ placer = 0,
+ row = Math.floor(rowRaw / invaderMultiplier);
+ if (row >= blocks.length) return [];
+ for (var i = 0; i < blocks[row].length; i++) {
+ var tmpContent = blocks[row][i] * invaderMultiplier;
+ for (var j = 0; j < invaderMultiplier; j++) textRow[placer + j] = tmpContent + j;
+ placer += invaderMultiplier;
+ }
+ return textRow;
+ }
+
+ // Write Text
+ // -----------
+ function createInvaders() {
+ var invaders = [];
+
+ var i = blocks.length * invaderMultiplier;
+ while (i--) {
+ var j = getPixelRow(i);
+ for (var k = 0; k < j.length; k++) {
+ invaders.push(new Invader({
+ x: j[k] * invaderSize,
+ y: i * invaderSize
+ }));
+ }
+ }
+ return invaders;
+ }
+
+ // Start game
+ // ----------
+ window.addEventListener('load', function() {
+
+ var invaderAsset = new Image;
+ invaderAsset.onload = function() {
+
+ invaderCanvas = document.createElement('canvas');
+ invaderCanvas.width = invaderSize;
+ invaderCanvas.height = invaderSize;
+ invaderCanvas.getContext("2d").drawImage(invaderAsset, 0, 0);
+
+ // Game Creation
+ canvas = document.getElementById("space-invaders");
+ screen = canvas.getContext('2d');
+
+ initGameStart();
+ loop();
+
+ };
+ invaderAsset.src = "//stillh.art/project/spaceInvaders/invader.gif";
+
+ });
+
+ window.addEventListener('resize', function() {
+ initGameStart();
+ });
+ document.getElementById('restart').addEventListener('click', function() {
+ initGameStart();
+ });
+
+ function initGameStart() {
+ if (window.innerWidth > 1200) {
+ screen.canvas.width = 1200;
+ screen.canvas.height = 500;
+ gameSize = {
+ width: 1200,
+ height: 500
+ };
+ invaderMultiplier = 3;
+ initialOffsetInvader = 420;
+ } else if (window.innerWidth > 800) {
+ screen.canvas.width = 900;
+ screen.canvas.height = 600;
+ gameSize = {
+ width: 900,
+ height: 600
+ };
+ invaderMultiplier = 2;
+ initialOffsetInvader = 280;
+ } else {
+ screen.canvas.width = 600;
+ screen.canvas.height = 300;
+ gameSize = {
+ width: 600,
+ height: 300
+ };
+ invaderMultiplier = 1;
+ initialOffsetInvader = 140;
+ }
+
+ kills = 0;
+ invaderAttackRate = 0.999;
+ invaderSpeed = 20;
+ spawnDelayCounter = invaderSpawnDelay;
+
+ game = new Game();
+ }
+
+ function loop() {
+ game.update();
+ game.draw();
+
+ requestAnimationFrame(loop);
+ }
+
+})();
\ No newline at end of file
diff --git a/Invaders/style.css b/Invaders/style.css
new file mode 100644
index 0000000..d446a26
--- /dev/null
+++ b/Invaders/style.css
@@ -0,0 +1,6 @@
+.center{text-align:center}
+#space-invaders {
+ margin: 0 auto;
+ display: block;
+ background: white
+}
\ No newline at end of file