var timercount = 0;
var shipx = 240;
var shipy = 500;
var shipwidth = 3;
var shipheight = 5;
var vectorx = 0;
var vectory = 0;
var is_key_left = 0;
var is_key_right = 0;
var is_key_up = 0;
var is_key_down = 0;
var is_firing = 0;
var cycle_step = 0;
var lives = 3;
var life_state = "Invincible";
var state_counter = 0;
var msec_frame = 30;
var challenge_mode = 0;

var is_paused = 0;
var current_wave = 0;
var current_score = 0;
var free_life = 1000;
var time_count = 0;
var wave_count = 0;
var wait_clear = 0;

function createCookie (name, value, days) {
  if (days) {
    var date = new Date();
    date.setTime (date.getTime() + (days * 24 * 60 * 60 * 1000));
    var expires = "; expires=" + date.toGMTString();
  }
  else var expires = "";
  document.cookie = name + "=" + value + expires + "; path=/";
}

function readCookie (name) {
  var nameEQ = name + "=";
  var ca = document.cookie.split (';');
  for (var i = 0; i < ca.length; i++) {
    var c = ca[i];
    while (c.charAt (0) == ' ') c = c.substring (1, c.length);
    if (c.indexOf (nameEQ) == 0) return c.substring (nameEQ.length, c.length);
  }
  return null;
}

var high_score = readCookie ("high_score");
if (high_score == null) high_score = 0;
var challenge_mode_unlocked = readCookie ("challenge_mode_unlocked");
if (challenge_mode_unlocked == null) challenge_mode_unlocked = 0;

var shotx = 0;
var shoty = 0;
var shotangle = 0;

var power1 = 0;
var power2 = 0;
var is_chrome = navigator.userAgent.toLowerCase().indexOf('chrome') > -1;
var is_safari =  false;
if (!is_chrome) is_safari = navigator.userAgent.toLowerCase().indexOf('safari') > -1;
// Weird... the user agent string for Chrome includes safari

var bg_music = 0;
var tunez = new Array;
var sfx = new Array;
if (!is_safari) {
  tunez[1] = document.createElement ('audio');
  tunez[2] = document.createElement ('audio');
  tunez[3] = document.createElement ('audio');
  tunez[4] = document.createElement ('audio');

  tunez[1].src = "music/superluminal_theme.ogg";
  tunez[2].src = "music/boss_battle.ogg";
  tunez[3].src = "music/station_rave.ogg";
  tunez[4].src = "music/rigel.ogg";
  tunez[1].load();
  tunez[2].load();
  tunez[3].load();
  tunez[4].load();
  tunez[1].addEventListener ('ended', function() {
    this.currentTime = 0;
  }, false);
  tunez[2].addEventListener ('ended', function() {
    this.currentTime = 0;
  }, false);
  tunez[3].addEventListener ('ended', function() {
    this.currentTime = 0;
  }, false);
  tunez[4].addEventListener ('ended', function() {
    this.currentTime = 0;
  }, false);

  sfx[0] = document.createElement ('audio');
  sfx[1] = document.createElement ('audio');
  sfx[2] = document.createElement ('audio');
  sfx[3] = document.createElement ('audio');
  sfx[4] = document.createElement ('audio');
  sfx[0].src = "sfx/splosion.ogg";
  sfx[1].src = "sfx/splosion.ogg";
  sfx[2].src = "sfx/bigboom.ogg";
  sfx[3].src = "sfx/powerup.ogg";
  sfx[4].src = "sfx/boink.ogg";
  for (var i = 0; i <= 4; i++) sfx[i].load();
}

var code_progress = 0;

current_wave = 6;
// For testing purposes:
if (location.href.indexOf ('testing') > -1) current_wave = prompt ("Test wave:", 6);
if (current_wave > 6) {
  current_wave = 6;
  challenge_mode = 1;
}
current_wave--;

// Use some o' them mathematical skillz to calculate the angle to a target, and/or vectors for an angle
function ontarget (x1, y1, x2, y2, velocity, thisangle) {
  if (thisangle == null) shotangle = Math.atan2 (x2 - x1, y2 - y1);
  else shotangle = thisangle;
  shotx = velocity * Math.sin (shotangle);
  shoty = velocity * Math.cos (shotangle);
}

// Make a fiery explosion for either your death or your enemies' death. Mostly your death. n00b.
function make_splosion (splodex, splodey, basedeltax, basedeltay, intensity, scatter, splodetype) {
  for (var i = 0; i < intensity; i++) {
    ontarget (0, 0, 0, 0, Math.random() * scatter, Math.random() * Math.PI * 2);
    Enemies.push (new Enemy (splodetype, splodex, splodey, basedeltax + shotx, basedeltay + shoty));
  }
}

// Show the enemy's health bar
function show_health_bar (curhp, maxhp) {
  var fillperc = Math.floor (curhp / maxhp * 480);
  ctx.fillStyle = "rgba(" + Math.floor (255 - (fillperc / 1.8823)) + ",0," + Math.floor (fillperc / 1.8823) + ",0.6)";
  ctx.fillRect (0, 0, fillperc, 8);
}

// Show the user which keys to press to not die
function show_controls() {
  ctx.font = "17px Arial";
  
  if (challenge_mode_unlocked == 1 && current_wave == 5) {
    ctx.fillStyle = "rgb(192,192,255)";
    ctx.fillText ("Press C three times to toggle Challenge Mode", 10, 490);
  }

  ctx.fillStyle = "rgb(0,192,255)";  
  ctx.fillText ("Movement: Arrow keys or WASD", 10, 530);
  ctx.fillText ("Weapon 1: Ctrl, Z or numpad 0", 10, 550);
  ctx.fillText ("Weapon 2: Shift, X or numpad .", 10, 570);
  ctx.fillText ("Pause: Space bar", 10, 590);
  ctx.fillText ("The red square in the middle of your ship is your hitbox", 10, 610);
}

// My god! It's full of stars!
function Star (startx, starty, startspeed) {
  this.x = startx;
  this.y = starty;
  this.speed = startspeed;
}

var Stars = new Array;

// Rift walls, for the last level (ZOMG spoiler)
function RiftPoint (startx, starty, startheight, startspeed) {
  this.x = startx;
  this.y = starty;
  this.height = startheight;
  this.speed = startspeed;
}
var Rift = new Array;
Rift.push (new Array);
Rift.push (new Array);
Rift.push (new Array);

// Other background types defined here
var background_type = "Stars";
var background_type2 = "";

function Background_element (eltype, startx, starty, eldeltax, eldeltay) {
  this.type = eltype;
  this.x = startx;
  this.y = starty;
  this.deltax = eldeltax;
  this.deltay = eldeltay;
}

var Background_elements = new Array;

// Counter to track approach to planet in front of Blue Giant
var approach_count = 0;

Background_element.prototype.draw = function() {
  if (this.type == "Station") {
    ctx.fillStyle = "rgb(32,32,64)";
    ctx.fillRect (0, this.y - 320, 150, 320);
    ctx.fillRect (330, this.y - 320, 150, 320);
    ctx.fillRect (150, this.y - 320, 180, 110);
    ctx.fillRect (150, this.y - 110, 180, 110);
    ctx.strokeStyle = "rgb(64,64,128)";
    ctx.strokeRect (150, this.y - 210, 180, 100);
    ctx.strokeRect (0, this.y - 320, 480, 0);
    ctx.fillStyle = "rgba(32,0,0,0.4)";
    ctx.fillRect (150, this.y - 210, 180, 100);
    
    for (var i = 0; i < 8; i++) {
      if (cycle_step < 16) ctx.fillStyle = "rgb(128," + (cycle_step * 8) + ",0)";
      else ctx.fillStyle = "rgb(128," + ((32 - cycle_step) * 8) + ",0)";
      ctx.fillRect (30, this.y - ((i + 1) * 40) + 5, 30, 30);
      ctx.strokeRect (30, this.y - ((i + 1) * 40) + 5, 30, 30);
      ctx.fillRect (420, this.y - ((i + 1) * 40) + 5, 30, 30);
      ctx.strokeRect (420, this.y - ((i + 1) * 40) + 5, 30, 30);
    }
  }
  else if (this.type == "Hangar") {
    ctx.fillStyle = "rgb(32,32,64)";
    ctx.strokeStyle = "rgb(64,64,128)";
    ctx.fillRect (0, this.y - 320, 480, 320);
    for (var i = 0; i < 4; i++) {
      if (cycle_step < 16) ctx.fillStyle = "rgb(128," + (cycle_step * 8) + ",0)";
      else ctx.fillStyle = "rgb(128," + ((32 - cycle_step) * 8) + ",0)";
      if (i % 2 == 0) {
        ctx.strokeRect (100, this.y - i * 80 - 60, 40, 30);
        ctx.strokeRect (340, this.y - i * 80 - 60, 40, 30);
        ctx.fillRect (160, this.y - i * 80 - 60, 160, 10);
        ctx.strokeRect (160, this.y - i * 80 - 60, 160, 10);
      }
      ctx.fillRect (60, this.y - i * 80 - 31, 10, 10);
      ctx.fillRect (410, this.y - i * 80 - 31, 10, 10);
      ctx.strokeRect (60, this.y - i * 80 - 31, 10, 10);
      ctx.strokeRect (410, this.y - i * 80 - 31, 10, 10);

      ctx.strokeRect (50, this.y - i * 80 - 70, 0, 50);
      ctx.strokeRect (430, this.y - i * 80 - 70, 0, 50);
      ctx.strokeRect (50, this.y - i * 80 - 70, 380, 0);
    }
  }
}

Background_element.prototype.update = function() {
  if (is_paused) return 0;
  this.x += this.deltax;
  this.y += this.deltay;
  if ((this.type == "Station" || this.type == "Hangar") && this.y > 960) {
    if (background_type == "Station" || background_type == "Hangar") {
      this.type = background_type;
      this.y -= 960;
    }
    else return 1;
  }
  return 0;
}

// Make a bullet - three types of bullets. They're lasers so can you call them bullets?
function Bullet (startx, starty, bullettype, bulletsideways) {
  this.x = startx;
  this.y = starty;
  this.type = bullettype;
  
  if (bulletsideways != null) this.deltax = bulletsideways;
  else this.deltax = 0;
  
  if (bullettype == 1) this.deltay = -12;
  else if (bullettype == 2) this.deltay = -10;
  else if (bullettype == 3) this.deltay = -9;
  else {
    this.deltax = 0;
    this.deltay = 0;
    if (bullettype >=4 && bullettype <= 6) this.deltay = -7;
    else if (bullettype >= 9 && bullettype <= 11) this.deltay = 7;
    if (bullettype == 4 || bullettype == 7 || bullettype == 9) this.deltax = -7;
    else if (bullettype == 6 || bullettype == 8 || bullettype == 11) this.deltax = 7;
  }
}

// Bullets in motion
Bullet.prototype.update = function() {
  this.x += this.deltax;
  this.y += this.deltay;
  
  if (this.type == 2) this.deltay -= 0.5;
  
  var testdeltay = this.deltay;
  if (this.type > 3) testdeltay = 0;
  
  if (this.x < -5 || this.x > 485 || this.y < -25 || this.y > 645) return 1;

  for (var i = 0; i < Enemies.length; i++) {
    if (Enemies[i].hp != 9001 && Enemies[i].hp > 0) {
      if (this.x > Enemies[i].x - Enemies[i].width &&
          this.x <= Enemies[i].x + Enemies[i].width &&
          this.y > Enemies[i].y - Enemies[i].height + testdeltay &&
          this.y <= Enemies[i].y + Enemies[i].height) {
        Enemies[i].hp--;
        if (Enemies[i].type != "Wall") {
          current_score++;
          if (challenge_mode == 1) current_score++;
          if (Enemies[i].type == "Sweeper") {
            Enemies[i].deltay -= 2.5;
            if (Enemies[i].deltay < -3) Enemies[i].deltay = -3;
          }
        }
        if (Enemies[i].hp <= 0) {
          var boss_splode = 0;
          if (Enemies[i].type == "Wing") {
            Enemies[i].hp = 2;
            Enemies[i].age = 0;
            Enemies.push (new Enemy ("Splosion", Enemies[i].x, Enemies[i].y, Enemies[i].deltax, Enemies[i].deltay));
            if (!is_safari) {
              if (cycle_step % 16 < 8) sfx[0].play();
              else sfx[1].play();
            }
            Enemies[i].x = Enemies[0].x;
            Enemies[i].y = Enemies[0].y;
            Enemies[0].hp--;
            if (Enemies[0].hp <= 0) {
              boss_splode = 1;
              Enemies[0].hp = 9001;
              Enemies[0].age = 0;
              Enemies[0].width = 0;
              Enemies[0].type = "Splosion";
              make_splosion (Enemies[0].x, Enemies[0].y, Enemies[0].deltax, Enemies[0].deltay, 10, 4, "Splodelet");
            }
          }
          else {
            if (Enemies[i].type != "Roid") {
              current_score += 4;
              if (challenge_mode == 1) current_score += 9;
            }
            if (Enemies[i].type == "Spiderboss" || Enemies[i].type == "Old Boss" || Enemies[i].type == "New Boss" || Enemies[i].type == "Crossfire" || Enemies[i].type == "Squad Leader") {
              boss_splode = 1;
            }
            Enemies[i].hp = 9001;
            Enemies[i].age = 0;
            Enemies[i].width = 0;
            if (Enemies[i].type == "Bigroid") make_splosion (Enemies[i].x, Enemies[i].y, Enemies[i].deltax, Enemies[i].deltay, 7, 9, "Roid");
            else make_splosion (Enemies[i].x, Enemies[i].y, Enemies[i].deltax, Enemies[i].deltay, 10, 4, "Splodelet");
            if (!is_safari) {
              if (cycle_step % 16 < 8) sfx[0].play();
              else sfx[1].play();
            }
            Enemies[i].type = "Splosion";
          }
          if (boss_splode == 1) {
            if (!is_safari) sfx[2].play();
            for (var kaboom = 0; kaboom < Enemies.length; kaboom++) {
              if (Enemies[kaboom].type == "Wing" || Enemies[kaboom].type == "Bomb" || Enemies[kaboom].type == "Bullet" ||
                  Enemies[kaboom].type == "Blue Bullet" || Enemies[kaboom].type == "Green Bullet" || Enemies[kaboom].type == "Wall") {
                Enemies[kaboom].hp = 9001;
                Enemies[kaboom].age = 0;
                Enemies[kaboom].width = 0;
                Enemies[kaboom].height = 8;
                Enemies[kaboom].type = "Splosion";
              }
            }
          }
        }
        if (this.type > 3) return 0;
        return 1;
      }
    }
  }

  return 0;
}

// Draw the magnificently detailed bullets onscreen
Bullet.prototype.draw = function() {
  if (this.type == 1) {
    ctx.fillStyle = "rgb(0,128,255)";
    ctx.fillRect (this.x, this.y, 2, 10);
  }
  else if (this.type == 2) {
    ctx.fillStyle = "rgb(255,0,255)";
    ctx.fillRect (this.x, this.y, 2, Math.abs (this.deltay * 0.8));
  }
  else if (this.type == 3) {
    ctx.fillStyle = "rgba(0,64,255,0.6)";
    ctx.fillRect (this.x, this.y, 2, 9);
  }
  else if (cycle_step % 2 == 0) {
    ctx.fillStyle = "rgba(255,255,192,0.6)";
    ctx.beginPath()
    ctx.arc (this.x, this.y, 5, 0, Math.PI * 2, 0);
    ctx.fill();
  }
}

// Bad guys go here
function Enemy (enemytype, enemyx, enemyy, startdeltax, startdeltay, newwidth, newheight, newhp) {
  this.type = enemytype;
  this.x = enemyx;
  this.y = enemyy;
  this.targetx = shipx;
  this.targety = shipy;
  if (this.type == "Bullet" || this.type == "Blue Bullet" || this.type == "Green Bullet") {
    this.width = 1;
    this.height = 1;
    this.hp = 9001;
    this.deltax = 0;
    this.deltay = 0;
  }
  else if (this.type == "Triangle" || this.type == "Wing") {
    this.hp = 2;
    this.width = 6;
    this.height = 7;
    this.deltax = 0;
    if (this.type == "Wing") this.deltay = 0;
    else this.deltay = 6;
    this.targetx = 0;
    this.targety = 0;
  }
  else if (this.type == "Splosion" || this.type == "Splodelet") {
    this.hp = 9001;
    this.width = 0;
    this.height = 10;
    this.deltax = 0;
    this.deltay = 0;
  }
  else if (this.type == "Cross") {
    this.hp = 6;
    this.width = 25;
    this.height = 20;
    this.deltax = 0;
    this.deltay = 7;
  }
  else if (this.type == "Wyvern") {
    this.hp = 3;
    this.width = 9;
    this.height = 9;
    this.deltax = 0;
    this.deltay = 6;
  }
  else if (this.type == "Fireball") {
    this.hp = 1;
    this.width = 8;
    this.height = 8;
    this.deltax = 0;
    this.deltay = 0;
  }
  else if (this.type == "Roid") {
    this.hp = 2;
    this.width = 7;
    this.height = 7;
    this.deltax = 0;
    this.deltay = 0;
  }
  else if (this.type == "Bigroid") {
    this.hp = 3;
    this.width = 20;
    this.height = 20;
    this.deltax = 0;
    this.deltay = 0;
  }
  else if (this.type == "Turret") {
    this.hp = 4;
    this.width = 12;
    this.height = 12;
    this.deltax = 0;
    this.deltay = 9;
  }
  else if (this.type == "Sweeper") {
    this.hp = 25;
    this.width = 25;
    this.height = 10;
    this.deltax = 0;
    this.deltay = 3;
  }
  else if (this.type == "Power1" || this.type == "Power2") {
    this.hp = 9001;
    this.width = 14;
    this.height = 14;
    this.deltax = 0;
    this.deltay = 0;
  }
  else if (this.type == "Wall") {
    // Walls will usually have other default values set for width, height, and speed, but just in case...
    this.hp = 1000;
    this.width = 50;
    this.height = 10;
    this.deltax = 0;
    this.deltay = 9;
  }
  else if (this.type == "Spiderboss") {
    this.hp = 125;
    this.width = 25;
    this.height = 20;
    this.deltax = 0;
    this.deltay = 6;
  }
  else if (this.type == "Crossfire") {
    this.hp = 300;
    this.width = 30;
    this.height = 30;
    this.deltax = 0;
    this.deltay = 3;
  }
  else if (this.type == "Old Boss") {
    this.hp = 80;
    this.width = 20;
    this.height = 8;
    this.deltax = 0;
    this.deltay = 2;
  }
  else if (this.type == "New Boss") {
    this.hp = 200;
    this.width = 20;
    this.height = 8;
    this.deltax = 0;
    this.deltay = 2;
  }
  else if (this.type == "Squad Leader") {
    this.hp = 500;
    this.width = 6;
    this.height = 7;
    this.deltax = 0;
    this.deltay = 6;
  }
  else if (this.type == "Bomb") {
    this.hp = 9001;
    this.width = 8;
    this.height = 8;
    this.deltax = 0;
    this.deltay = 0;
  }
  else if (this.type == "Title" || this.type == "You Suck") {
    this.hp = 9001;
    this.width = 0;
    this.height = 0;
    this.deltax = 0;
    this.deltay = 0;
  }
  else {
    // Text (very slowly moves downward as it fades in/out)
    this.hp = 9001;
    this.width = 0;
    this.height = 0;
    this.deltax = 0;
    this.deltay = 0.2;
  }

  // Values can be overwritten
  if (startdeltax != null) this.deltax += startdeltax;
  if (startdeltay != null) this.deltay += startdeltay;
  if (newwidth != null) this.width = newwidth;
  if (newheight != null) this.height = newheight;
  if (newhp != null) this.hp = newhp;
  
  this.age = 0;
}

// Artificial stupidity for enemies. If they were intelligent they'd attack you all at once instead of in small waves.
Enemy.prototype.update = function() {
  var checkx1 = -15;
  var checky1 = -1500;
  var checkx2 = 495;
  var checky2 = 655;
  this.x += this.deltax;
  this.y += this.deltay;

  if (this.type == "Bullet" || this.type == "Blue Bullet" || this.type == "Green Bullet") {
    if (this.age > 1500) return 1;
    checkx1 = -4;
    checkx2 = 484;
    checky1 = -4;
    checky2 = 644;
  }
  else if (this.type == "Fireball") {
    checkx1 = -215;
    checkx2 = 695;
    checky2 = 855;
  }
  else if (this.type == "Wall") {
    checky1 = -2500;
    checky2 = 3000;
  }
  else if (this.type == "Roid" && this.age > 1000) return 1;
  
  if (this.type != "Wing") {
    // For final boss, all 21 wingmen must exist at all times
    if (this.x < checkx1 || this.x > checkx2 || this.y < checky1 || this.y > checky2) return 1;
  }

  if (this.type == "Triangle") {
    if (this.y > 50 && this.y < 280) {
      if (this.x > shipx) this.deltax--;
      else if (this.x < shipx) this.deltax++;
      if (this.deltax > 3) this.deltax = 3;
      else if (this.deltax < -3) this.deltax = -3;
    }
    if (shipy > this.y && Math.floor (Math.random() * 81) == 0) {
      shotangle = Math.atan2 (this.deltax, this.deltay);
      ontarget (this.x, this.y + this.height, 0, 0, 10, shotangle);
      Enemies.push (new Enemy ("Bullet", this.x, this.y + this.height, shotx, shoty));
    }
  }
  else if (this.type == "Cross") {
    if (this.age == 20) this.deltay = 0;
    if (this.age == 25 || this.age == 30) {
      this.targetx = Math.random() * 480;
      this.targety = Math.random() * 420 + 220;
      var bulltype = Math.floor (Math.random() * 3);
      if (bulltype == 0) bulltype = "Blue Bullet";
      else if (bulltype == 1) bulltype = "Green Bullet";
      else bulltype = "Bullet";
      for (var b = 0; b < 5; b++) {
        ontarget (this.x, this.y, this.targetx, this.targety, 8 - b);
        Enemies.push (new Enemy (bulltype, this.x, this.y, shotx, shoty));
        ontarget (this.x - 19, this.y, this.targetx, this.targety, 8 - b);
        Enemies.push (new Enemy (bulltype, this.x - 19, this.y, shotx, shoty));
        ontarget (this.x + 19, this.y, this.targetx, this.targety, 8 - b);
        Enemies.push (new Enemy (bulltype, this.x + 19, this.y, shotx, shoty));
        ontarget (this.x, this.y - 19, this.targetx, this.targety, 8 - b);
        Enemies.push (new Enemy (bulltype, this.x, this.y - 19, shotx, shoty));
        ontarget (this.x, this.y + 19, this.targetx, this.targety, 8 - b);
        Enemies.push (new Enemy (bulltype, this.x, this.y + 19, shotx, shoty));
      }
    }
    if (this.age == 40) {
      ontarget (this.x, this.y, shipx, shipy, 5);
      this.deltax = shotx;
      this.deltay = shoty;
    }
  }
  else if (this.type == "Wyvern") {
    ontarget (this.x, this.y, shipx, shipy, 7);
    if (this.age % 12 == 0) {
      Enemies.push (new Enemy ("Bullet", this.x, this.y, shotx, shoty));
    }

    if (cycle_step % 16 == 0) {
      if (this.age > 60) {
        this.deltay += 0.8;
        if (this.x < 110) this.deltax += 2.5;
        else if (this.x > 370) this.deltax -= 2.5;
        else this.deltax += Math.floor (Math.random() * 5) - 2;
        
        if (this.age < 600) {
          if (this.deltax > 5) this.deltax = 5;
          else if (this.deltax < -5) this.deltax = -5;
          if (this.deltay > 5.5) this.deltay = 0 - (this.deltay * .7);
        }
      }
    }
  }
  else if (this.type == "Fireball") {
    // No two heat-seeking oranges are not on fire
    if (this.age < 500) {
      ontarget (this.x, this.y, shipx, shipy, 0.6);
      this.deltax += shotx;
      this.deltay += shoty;
    }
    if (this.deltax > 8) this.deltax = 8;
    else if (this.deltax < -8) this.deltax = -8;
    if (this.deltay > 8) this.deltay = 8;
    else if (this.deltay < -8) this.deltay = -8;
    
    Enemies.push (new Enemy ("Splodelet", this.x, this.y, this.deltax / 2, this.deltay / 2));
  }
  else if (this.type == "Turret") {
    // Simply lock on and fire a volley at you after waiting 40 frames
    if (this.age >= 40 && this.age < 43) {
      ontarget (this.x, this.y, shipx, shipy, 8);
      Enemies.push (new Enemy ("Bullet", this.x + shotx, this.y + shoty, shotx, shoty));
    }
  }
  else if (this.type == "Sweeper") {
    // They upgraded this purple guy since the last game
    if (this.deltay < 3) this.deltay += 0.25;
    if (this.deltay > 3) this.deltay = 3;
    if (cycle_step % 16 == 0) {
      Enemies.push (new Enemy ("Bullet", this.x, this.y + 25, 0, 7));
      Enemies.push (new Enemy ("Bullet", this.x - 25, this.y - 25, -7, 0));
      Enemies.push (new Enemy ("Bullet", this.x + 25, this.y - 25, 7, 0));
      Enemies.push (new Enemy ("Green Bullet", this.x - 17, this.y + 7, -2, 5.7));
      Enemies.push (new Enemy ("Green Bullet", this.x + 17, this.y + 7, 2, 5.7));
      Enemies.push (new Enemy ("Blue Bullet", this.x, this.y + 25, -1, 6));
      Enemies.push (new Enemy ("Blue Bullet", this.x, this.y + 25, 1, 6));
    }
  }
  else if (this.type == "Power1" || this.type == "Power2") {
    if (this.deltay < 4) this.deltay += 0.25;
    if (this.x > shipx + 1) this.deltax = -1;
    else if (this.x < shipx - 1) this.deltax = 1;
    else this.deltax = 0;
  }
  else if (this.type == "Spiderboss") {
    // Does whatever a spiderboss does
    if (this.age == 50) this.deltay = 0;
    if (this.age >= 60) {
      if (this.age % 60 == 0) {
        ontarget (this.x, this.y, Math.random() * 380 + 50, Math.random() * 450 + 50, 2);
        this.deltax = shotx;
        this.deltay = shoty;
      }
      if (this.x < 10) this.deltax = 0.5;
      else if (this.x > 470) this.deltax = -0.5;
      if (this.y < 10) this.deltay = 0.5;
      else if (this.y > 500) this.deltay = -0.5;
    }
    if (cycle_step % 2 == 0) {
      ontarget (0, 0, 0, 0, 7.5, cycle_step / 5.09);
      Enemies.push (new Enemy ("Bullet", this.x + shotx * 4, this.y + shoty * 4, shotx, shoty));
    }
    else {
      ontarget (0, 0, 0, 0, 7.5, ((cycle_step - 16) % 32) / 5.09);
      Enemies.push (new Enemy ("Blue Bullet", this.x + shotx * 4, this.y + shoty * 4, shotx, shoty));
    }
    if (this.age > 50 && this.age % 40 < 10 + challenge_mode * 20) {
      if (this.age % 40 == 0) {
        this.targetx = shipx;
        this.targety = shipy;
      }
      ontarget (this.x, this.y, this.targetx, this.targety, 6);
      Enemies.push (new Enemy ("Green Bullet", this.x + shotx * 3, this.y + shoty * 3, shotx, shoty));
    }
  }
  else if (this.type == "Crossfire") {
    this.x = shipx;
    if (this.y > 0) {
      this.deltay = 0;
      this.y = 0;
    }
    if (this.age >= 40) {
      var mod80 = this.age % 80;
      for (var i = 0; i < 2.5; i += 0.3) {
        if (mod80 == 40) Enemies.push (new Enemy ("Bullet", this.x, this.y + 30, 0, 7 - i));
        else if (mod80 == 60) Enemies.push (new Enemy ("Bullet", 0, shipy, 7 - i, 0));
        else if (mod80 == 0) Enemies.push (new Enemy ("Bullet", 479, shipy, -7 + i, 0));
        else if (mod80 == 20) Enemies.push (new Enemy ("Bullet", this.x, 639, 0, -7 + i));
      }
    }
    if (this.age % 140 == 139) {
      if (challenge_mode == 1) {
        Enemies.push (new Enemy ("Wall", Math.floor (Math.random() * 390) + 45, -50, 0, 0, 90, 20));
      }
      else {
        if (Math.floor (Math.random() * 2) == 0) Enemies.push (new Enemy ("Wall", 90, -50, 0, 0, 90, 15));
        else Enemies.push (new Enemy ("Wall", 389, -50, 0, 0, 90, 15));
      }
    }
  }
  else if (this.type == "Old Boss" || this.type == "New Boss") {
    if (this.y > 42) {
      this.deltay = 0;
      if (this.x < 100) this.deltax++;
      else if (this.x > 380) this.deltax--;
      else {
        if (cycle_step % 8 == 0) this.deltax += Math.floor (Math.random() * 7) - 3;
      }
      
      if (this.type == "Old Boss") {
        if (this.deltax > 8) this.deltax = 8;
        if (this.deltax < -8) this.deltax = -8;
        var sixteen_cycle = cycle_step % 16;
        Enemies.push (new Enemy ("Bullet", this.x, this.y + 8, sixteen_cycle - 8, 6));
        if (cycle_step % 8 == 0) Enemies.push (new Enemy ("Bullet", this.x, this.y, 0, -6));
      }
      else {
        if (this.deltax > 5) this.deltax = 5;
        if (this.deltax < -5) this.deltax = -5;
        if (cycle_step == 0) {
          if (this.hp <= 100 && Math.floor (Math.random() * 2) == 0) ontarget (this.x, this.y + 8, Math.random() * 480, 400, 6);
          else ontarget (this.x, this.y + 8, Math.random() * 480, 640, 10);
            
          Enemies.push (new Enemy ("Bomb", this.x, this.y + 8, shotx, shoty));
        }
      }
    }
  }
  else if (this.type == "Wing") {
    var targx = Enemies[0].x + this.targetx - this.x;
    var targy = Enemies[0].y + this.targety - this.y;
    // Blindly follow squad leader's directions to move to certain targets 
    // A man chooses. A triangle obeys!!!
    var followdist = Math.sqrt (targx * targx + targy * targy);
    if (followdist > 7) followdist = 7;
        
    ontarget (0, 0, targx, targy, followdist);
    
    if (this.age >= 25) {
      this.deltax = shotx;
      this.deltay = shoty;
    }
    else {
      this.deltax = Enemies[0].deltax;
      this.deltay = Enemies[0].deltay;
    }
  }
  else if (this.type == "Squad Leader") {
    if (this.hp < 150) {
      // Final form! Very tight triangle
      final_boss_formation ("Triangle");
      for (var i = 1; i <= 21; i++) {
        Enemies[i].targetx *= 0.4;
        Enemies[i].targety *= 0.4;
      }
      for (var i = 0; i < Math.PI * 2; i += Math.PI / 2) {
        if (this.age % 50 < 20) {
          ontarget (0, 0, 0, 0, 7, i + (this.age % 25) / 40 + this.age / 200);
          Enemies.push (new Enemy ("Bullet", this.x, this.y, shotx, shoty));
        }
        else if (this.age % 25 < 20) {
          ontarget (0, 0, 0, 0, 7, i - (this.age % 25) / 40 + this.age / 200);
          Enemies.push (new Enemy ("Bullet", this.x, this.y, shotx, shoty));
        }
      }
      if (this.x > 280) this.deltax = -1;
      else if (this.x < 200) this.deltax = 1;
      
      if (this.y > 350) this.deltay = -1;
      else if (this.y < 300) this.deltay = 1;
      if (this.age % 50 == 0) {
        ontarget (this.x, this.y, Math.random() * 380 + 50, Math.random() * 450 + 50, 1.2);
        this.deltax = shotx;
        this.deltay = shoty;
      }
      if (this.age % 50 < 25) {
        ontarget (0, 0, 0, 0, 4, this.age / 70);
        Enemies.push (new Enemy ("Green Bullet", this.x, this.y, shotx, shoty));
        Enemies.push (new Enemy ("Blue Bullet", this.x, this.y, 0 - shotx, 0 - shoty));
      }
    }
    else if (this.age == 40) {
      this.deltay = 0;
      final_boss_formation ("Triangle");
    }
    else if (this.age > 40) {
      if (this.age > 1700) this.age = 41;
      else if (this.age > 150 && this.age < 180) {
        if (this.age == 160) {
          if (this.x > 260) this.deltax = -2;
          else if (this.x < 220) this.deltax = 2;

          if (this.y > 340) this.deltay = -2;
          else if (this.y < 310) this.deltay = 2;
        }
        for (var i = 1; i <= 21; i++) {
          Enemies[i].targetx *= 1.05;
          Enemies[i].targety *= 1.05;
        }
      }
      else if (this.age >= 200 && this.age <= 500) {
        this.deltax = 0;
        this.deltay = 0;
        final_boss_formation ("Circles");
        if (this.age >= 250) {
          for (var i = 1; i <= 21; i++) {
            ontarget (Enemies[i].x, Enemies[i].y, Enemies[0].x, Enemies[0].y, 6);
            if (Enemies[i].age >= 25 && this.age % 30 < 15) {
              if (i <= 10) Enemies.push (new Enemy ("Blue Bullet", Enemies[i].x, Enemies[i].y, shotx, shoty));
              else Enemies.push (new Enemy ("Bullet", Enemies[i].x, Enemies[i].y, shotx, shoty));
            }
          }
        }
      }
      else if (this.age == 501) final_boss_formation ("Triangle");
      else if (this.age >= 800 && this.age <= 1100) {
        if (this.age > 1000) {
          for (var i = 1; i <= 21; i++) {
            Enemies[i].targetx *= 1.02;
            Enemies[i].targety *= 1.02;
          }
        }
        else final_boss_formation ("Squares");
        if (this.age > 850) {
          if (cycle_step % 4 == 0) {
            for (var i = 1; i <= 4; i++) {
              if (Enemies[i].age >= 25) Enemies.push (new Enemy ("Green Bullet", Enemies[i].x, Enemies[i].y, 7, 0));
              if (Enemies[4 + i].age >= 25) Enemies.push (new Enemy ("Green Bullet", Enemies[4 + i].x, Enemies[4 + i].y, -7, 0));
              if (Enemies[8 + i].age >= 25) Enemies.push (new Enemy ("Blue Bullet", Enemies[8 + i].x, Enemies[8 + i].y, 0, 7));
              if (Enemies[12 + i].age >= 25) Enemies.push (new Enemy ("Blue Bullet", Enemies[12 + i].x, Enemies[12 + i].y, 0, -7));
            }
          }
          if (cycle_step % 8 == 0) {
            for (var i = 17; i <= 21; i++) {
              if (Enemies[i].age >= 25) {
                if (this.age <= 1000) Enemies.push (new Enemy ("Bullet", Enemies[i].x, Enemies[i].y, 0 - Enemies[i].deltax, 0 - Enemies[i].deltay));
                else {
                  ontarget (Enemies[i].x, Enemies[i].y, this.x, this.y, 4);
                  Enemies.push (new Enemy ("Bullet", Enemies[i].x, Enemies[i].y, shotx, shoty));
                }
              }
            }
          }
        }
      }
      else if (this.age == 1430 || this.age == 1450) final_boss_formation ("Triangle");
      else if (this.age >= 1490 && this.age <= 1680) {
        if (Enemies[1].age >= 25) Enemies.push (new Enemy ("Blue Bullet", Enemies[1].x, Enemies[1].y, -5, -5));
        if (Enemies[6].age >= 25) Enemies.push (new Enemy ("Blue Bullet", Enemies[6].x, Enemies[6].y, 5, -5));
        if (Enemies[21].age >= 25) Enemies.push (new Enemy ("Blue Bullet", Enemies[21].x, Enemies[21].y, 0, 7));
        if (Enemies[12].age >= 25) Enemies.push (new Enemy ("Green Bullet", Enemies[12].x, Enemies[12].y, -5, 2.5));
        if (Enemies[15].age >= 25) Enemies.push (new Enemy ("Green Bullet", Enemies[15].x, Enemies[15].y, 5, 2.5));
        if (Enemies[9].age >= 25) Enemies.push (new Enemy ("Green Bullet", Enemies[9].x, Enemies[9].y, 0, -7));
        if (cycle_step % 16 == 0) {
          ontarget (Enemies[0].x, Enemies[0].y, shipx, shipy, 7);
          Enemies.push (new Enemy ("Bullet", Enemies[0].x, Enemies[0].y, shotx, shoty));
          Enemies.push (new Enemy ("Bullet", Enemies[0].x, Enemies[0].y, shotx * 0.85, shoty * 0.85));
          Enemies.push (new Enemy ("Bullet", Enemies[0].x, Enemies[0].y, 0 - shotx, 0 - shoty));
          Enemies.push (new Enemy ("Bullet", Enemies[0].x, Enemies[0].y, 0 - shotx * 0.85, 0 - shoty * 0.85));
        }
      }
      else {
        if (this.age % 50 == 0) {
          final_boss_formation ("Triangle");
          var attack_style = Math.floor (Math.random() * 2) + 1;
          if (attack_style == 1) {
            ontarget (this.x, this.y, shipx, shipy, 5);
            for (var i = 1; i <= 21; i++) {
              if (Enemies[i].age >= 25) Enemies.push (new Enemy ("Bullet", Enemies[i].x, Enemies[i].y, shotx, shoty));
            }
          }
          else {
            for (var i = 1; i <= 21; i++) {
              if (Math.floor (Math.random() * 4) == 1) {
                Enemies[i].targetx += Math.random() * 100 - 50;
                if (shipy < Enemies[i].y - 60) Enemies[i].targety -= 300;
                else if (shipy > Enemies[i].y + 60) Enemies[i].targety += 300;
                if (shipx < Enemies[i].x) Enemies[i].targetx -= Math.random() * 100;
                if (shipx > Enemies[i].x) Enemies[i].targetx += Math.random() * 100;
              }
            }
          }
        }
        else if (this.age % 50 == 25) {
          rtargx = Math.random() * 480;
          rtargy = Math.random() * 640;
          for (var i = 1; i <= 21; i++) {
            if (Enemies[i].age >= 25) {
              ontarget (Enemies[i].x, Enemies[i].y, rtargx, rtargy, 4);
              Enemies.push (new Enemy ("Bullet", Enemies[i].x, Enemies[i].y, shotx, shoty));
            }
          }
        }
      }
      
      if (this.age % 60 == 0) {
        ontarget (this.x, this.y, Math.random() * 380 + 50, Math.random() * 450 + 50, 2);
        this.deltax = shotx;
        this.deltay = shoty;
      }
      if (this.x < 50) this.deltax = 0.5;
      else if (this.x > 430) this.deltax = -0.5;
      if (this.y < 50) this.deltay = 0.5;
      else if (this.y > 450) this.deltay = -0.5;
    }
  }
  else if (this.type == "Bomb") {
    // Da bomb!
    this.deltax = this.deltax * 0.984;
    this.deltay = this.deltay * 0.984;
    if (this.deltay <= 1) {
      this.age = 0;
      this.width = 0;
      this.type = "Splosion";
      var splodetype = Math.floor (Math.random() * 3);
      if (splodetype == 0) splodetype = "Green Bullet";
      else if (splodetype == 1) splodetype = "Blue Bullet";
      else splodetype = "Bullet";
      
      for (var i = 0; i < Math.PI * 2; i += 0.525) {
        ontarget (0, 0, 0, 0, 5, i);
        Enemies.push (new Enemy (splodetype, this.x, this.y, shotx, shoty));
      }
      sfx[1].play();
    }
  }
  else if (this.type == "Roid") {
    if (current_wave == 3) {
      this.deltay += 0.25;
      if (this.deltay >= 11) {
        this.type = "Splodelet";
        this.age = 0;
        this.width = 0;
        this.hp = 9001;
        make_splosion (this.x, this.y, 0, 3, 6, 4, "Splodelet");
      }
    }
  }
  else if (this.type == "Bigroid") {
    if (current_wave == 3 && this.y > -10) {
      this.deltay += 0.25;
      if (this.deltay >= 11) {
        this.type = "Splosion";
        this.age = 0;
        this.width = 0;
        this.hp = 9001;
        make_splosion (this.x, this.y, 0, 0, 6, 8, "Roid");
      }
    }
  }
  else if (this.type == "You Suck") {
    if (this.age >= 500) life_state = "Restart";
    else if (this.age >= 150 && challenge_mode == 1) life_state = "Restart";
  }
  else if (this.type != "Wall" && this.type != "Title") {
    // Text erases itself after 180 frames
    if (this.age > 180) return 1;
  }
  
  this.age++;

  if (this.type == "Splosion" && this.age > 15) return 1;
  if (this.type == "Splodelet" && this.age > 20) return 1;
  return 0;
}

// Video games are art. Check it below. Art everywhere in this bitch.
Enemy.prototype.draw = function() {
  if (this.type == "Bullet" || this.type == "Blue Bullet" || this.type == "Green Bullet") {
    if (this.type == "Blue Bullet") ctx.fillStyle = "rgba(0,128,255,0.6)";
    else if (this.type == "Green Bullet") ctx.fillStyle = "rgba(0,255,0,0.6)";
    else ctx.fillStyle = "rgba(255,0,0,0.6)";
    ctx.beginPath();
    ctx.arc (this.x, this.y, 3, 0, Math.PI * 2, 0);
    ctx.fill();
    ctx.beginPath();
    ctx.arc (this.x, this.y, 1.5, 0, Math.PI * 2, 0);
    ctx.fill();
  }
  else if (this.type == "Triangle" || this.type == "Wing" || this.type == "Squad Leader") {
    if (this.type == "Squad Leader") {
      show_health_bar (this.hp, 500);
    }
    else {
      if (this.type == "Triangle" || this.age >= 25) ctx.fillStyle = "rgb(255," + (128 * this.hp - 1) + ",0)";
      else {
        if (cycle_step % 2 == 0) ctx.fillStyle = "rgb(255,0,0)";
        else ctx.fillStyle = "rgb(255,255,255)";
      }
      ctx.beginPath();
      ctx.moveTo (this.x, this.y + this.height);
      ctx.lineTo (this.x - this.width, this.y - this.height);
      ctx.lineTo (this.x + this.width, this.y - this.height);
      ctx.fill();
    }
  }
  else if (this.type == "Splosion") {
    // Big yellow boom
    ctx.fillStyle = "rgba(255,255,0," + (1 - (this.age / 15)) + ")";
    ctx.beginPath();
    ctx.arc (this.x, this.y, this.age * (this.height / 5), 0, Math.PI * 2, 0);
    ctx.fill();
  }
  else if (this.type == "Splodelet") {
    // Cute little red boom
    ctx.fillStyle = "rgba(255,0,0," + (0.5 - (this.age / 40)) + ")";
    ctx.beginPath();
    ctx.arc (this.x, this.y, this.age * (this.height / 20), 0, Math.PI * 2, 0);
    ctx.fill();
  }
  else if (this.type == "Cross") {
    // The most dangerous combination of crosses and bullets since organized religion.
    ctx.strokeStyle = "rgb(0,96,0)";
    ctx.fillStyle  = "rgb(0,64,0)";
    
    ctx.strokeRect (this.x - 20, this.y - 20, 40, 40);
    ctx.fillRect (this.x - 20, this.y - 20, 40, 40);
    
    ctx.strokeStyle = "rgb(0,192,64)";
    ctx.fillStyle = "rgb(0,128,32)";
    ctx.strokeRect (this.x - 7, this.y - 25, 14, 50);
    ctx.strokeRect (this.x - 25, this.y - 7, 50, 14);
    ctx.fillRect (this.x - 7, this.y - 25, 14, 50);
    ctx.fillRect (this.x - 25, this.y - 7, 50, 14);
    ctx.fillStyle = "rgb(0,0,0)";

    // Holes what shoot bullets
    ctx.beginPath();
    ctx.arc (this.x, this.y, 3, 0, Math.PI * 2, 0);
    ctx.fill();
    ctx.beginPath();
    ctx.arc (this.x - 19, this.y, 3, 0, Math.PI * 2, 0);
    ctx.fill();
    ctx.beginPath();
    ctx.arc (this.x, this.y - 19, 3, 0, Math.PI * 2, 0);
    ctx.fill();
    ctx.beginPath();
    ctx.arc (this.x + 19, this.y, 3, 0, Math.PI * 2, 0);
    ctx.fill();
    ctx.beginPath();
    ctx.arc (this.x, this.y + 19, 3, 0, Math.PI * 2, 0);
    ctx.fill();
  }
  else if (this.type == "Wyvern") {
    // These guys should be quite annoying
    var tipx = 10;
    var tipy = 5;
    
    tipx += Math.sin (cycle_step / 5.09) * 4;
    tipy += Math.cos (cycle_step / 5.09) * 5;
    ctx.fillStyle = "rgb(0," + (80 * this.hp + 114) + ",128)";

    ctx.beginPath();
    ctx.moveTo (this.x - tipx, this.y - tipy);
    ctx.lineTo (this.x - 4, this.y - 8);
    ctx.lineTo (this.x, this.y - 6);
    ctx.lineTo (this.x + 4, this.y - 8);
    ctx.lineTo (this.x + tipx, this.y - tipy);
    ctx.lineTo (this.x, this.y - 3);
    ctx.fill();
    ctx.beginPath();
    ctx.moveTo (this.x - 4, this.y - 5);
    ctx.lineTo (this.x, this.y + 8);
    ctx.lineTo (this.x + 4, this.y - 5);
    ctx.fill();
  }
  else if (this.type == "Fireball") {
    // Flashing red and orange fireball of burny death
    ctx.fillStyle = "rgb(255," + Math.floor (Math.random() * 256) + ",0)";
    ctx.beginPath();
    ctx.arc (this.x, this.y, this.width, 0, Math.PI * 2, 0);
    ctx.fill();
  }
  else if (this.type == "Roid" || this.type == "Bigroid") {
    var curstep = this.x + cycle_step;
    var sizefactor = 1;
    
    if (this.type == "Bigroid") sizefactor = 2.2;
    
    var rx1 = Math.sin ((curstep % 32) / 5.09) * 9 * sizefactor;
    var ry1 = Math.cos ((curstep % 32) / 5.09) * 9 * sizefactor;
    var rx2 = Math.sin (((curstep + 6) % 32) / 5.09) * 12 * sizefactor;
    var ry2 = Math.cos (((curstep + 6) % 32) / 5.09) * 12 * sizefactor;
    var rx3 = Math.sin (((curstep + 12) % 32) / 5.09) * 8 * sizefactor;
    var ry3 = Math.cos (((curstep + 12) % 32) / 5.09) * 8 * sizefactor;
    var rx4 = Math.sin (((curstep + 19) % 32) / 5.09) * 9 * sizefactor;
    var ry4 = Math.cos (((curstep + 19) % 32) / 5.09) * 9 * sizefactor;
    var rx5 = Math.sin (((curstep + 25) % 32) / 5.09) * 9 * sizefactor;
    var ry5 = Math.cos (((curstep + 25) % 32) / 5.09) * 9 * sizefactor;
    
    if (Math.floor (this.x) % 2 == 0) {
      rx1 = 0 - rx1;
      rx2 = 0 - rx2;
      rx3 = 0 - rx3;
      rx4 = 0 - rx4;
      rx5 = 0 - rx5;
    }
    
    ctx.fillStyle = "rgb(64,64,64)";
    ctx.strokeStyle = "rgb(128,128,128)";
    ctx.beginPath();
    ctx.moveTo (this.x + rx1, this.y + ry1);
    ctx.lineTo (this.x + rx2, this.y + ry2);
    ctx.lineTo (this.x + rx3, this.y + ry3);
    ctx.lineTo (this.x + rx4, this.y + ry4);
    ctx.lineTo (this.x + rx5, this.y + ry5);
    ctx.fill();
    ctx.beginPath();
    ctx.moveTo (this.x + rx1, this.y + ry1);
    ctx.lineTo (this.x + rx2, this.y + ry2);
    ctx.lineTo (this.x + rx3, this.y + ry3);
    ctx.lineTo (this.x + rx4, this.y + ry4);
    ctx.lineTo (this.x + rx5, this.y + ry5);
    ctx.lineTo (this.x + rx1, this.y + ry1);
    ctx.stroke();
  }
  else if (this.type == "Turret") {
    // Note to anyone on the internets reading this. THERE IS NO "N" IN TURRET YOU IDIOTS!
    ctx.fillStyle = "rgb(64,64,64)";
    ctx.beginPath();
    ctx.arc (this.x, this.y, this.width, 0, Math.PI * 2, 0);
    ctx.fill();
    ontarget (this.x, this.y, shipx, shipy, this.width / 2);
    ctx.fillStyle = "rgb(0,0,0)";
    ctx.beginPath();
    ctx.arc (this.x + shotx, this.y + shoty, this.width / 4, 0, Math.PI * 2, 0);
    ctx.fill();
  }
  else if (this.type == "Sweeper") {
    // The purple T's from Superluminal 1 are back and meaner
    ctx.fillStyle = "rgb(192,0,192)";
    ctx.fillRect (this.x - 25, this.y - 25, 50, 10);
    ctx.fillRect (this.x - 5, this.y - 25, 10, 50);
    ctx.beginPath();
    ctx.moveTo (this.x, this.y - 20);
    ctx.lineTo (this.x + 15, this.y + 10);
    ctx.lineTo (this.x + 20, this.y + 5);
    ctx.lineTo (this.x + 5, this.y -25);
    ctx.fill();
    ctx.beginPath();
    ctx.moveTo (this.x, this.y - 20);
    ctx.lineTo (this.x - 15, this.y + 10);
    ctx.lineTo (this.x - 20, this.y + 5);
    ctx.lineTo (this.x - 5, this.y -25);
    ctx.fill();
  }
  else if (this.type == "Power1") {
    // Lazerus poweruppicus the first
    ctx.fillStyle = "rgb(255,255,255)";
    ctx.fillRect (this.x - 7, this.y - 7, 14, 14);
    if (cycle_step % 4 != 0) {
      ctx.fillStyle = "rgb(0,128,255)";
      ctx.fillRect (this.x - 6, this.y - 6, 12, 12);
    }
  }
  else if (this.type == "Power2") {
    // Lazerus poweruppicus the second
    ctx.fillStyle = "rgb(255,255,255)";
    ctx.fillRect (this.x - 7, this.y - 7, 14, 14);
    if (cycle_step % 4 != 0) {
      ctx.fillStyle = "rgb(255,0,255)";
      ctx.fillRect (this.x - 6, this.y - 6, 12, 12);
    }
  }
  else if (this.type == "Wall") {
    // We don't need no education
    ctx.fillStyle = "rgb(128,128,128)";
    ctx.fillRect (this.x - this.width, this.y - this.height, this.width * 2, this.height * 2);
    ctx.fillStyle = "rgb(96,96,96)";
    ctx.fillRect (this.x - this.width + 3, this.y - this.height + 3, this.width * 2 - 6, this.height * 2 - 6);
    // We don't need no thought control
  }
  else if (this.type == "Spiderboss") {
    // Creepy wriggly legs
    var legx = Math.sin (((cycle_step * 2) % 32) / 5.09) * 6;
    var legy = Math.cos (((cycle_step * 2) % 32) / 5.09) * 6;
    var kneex = legx / 2;
    var kneey = legy / 2;
    ctx.fillStyle = "rgb(144,0,144)";
       
    // Eight Legs
    ctx.beginPath();
    ctx.moveTo (this.x - 50 - legx, this.y - 25 + legy);
    ctx.lineTo (this.x - 30 - kneex, this.y - 35 + kneey);
    ctx.lineTo (this.x, this.y);
    ctx.lineTo (this.x - 30 - kneex, this.y - 30 + kneey);
    ctx.fill();
    ctx.beginPath();
    ctx.moveTo (this.x + 50 + legx, this.y - 25 - legy);
    ctx.lineTo (this.x + 30 + kneex, this.y - 35 - kneey);
    ctx.lineTo (this.x, this.y);
    ctx.lineTo (this.x + 30 + kneex, this.y - 30 - kneey);
    ctx.fill();
    
    ctx.beginPath();
    ctx.moveTo (this.x - 60 - legx, this.y - legy);
    ctx.lineTo (this.x - 40 - kneex, this.y - 15 - kneey);
    ctx.lineTo (this.x, this.y);
    ctx.lineTo (this.x - 40 - kneex, this.y - 10 - kneey);
    ctx.fill();
    ctx.beginPath();
    ctx.moveTo (this.x + 60 + legx, this.y + legy);
    ctx.lineTo (this.x + 40 + kneex, this.y - 15 + kneey);
    ctx.lineTo (this.x, this.y);
    ctx.lineTo (this.x + 40 + kneex, this.y - 10 + kneey);
    ctx.fill();
    
    ctx.beginPath();
    ctx.moveTo (this.x - 50 - legx, this.y + 40 + legy * 2);
    ctx.lineTo (this.x - 40 - kneex, this.y + 15 + kneey);
    ctx.lineTo (this.x, this.y);
    ctx.lineTo (this.x - 40 - kneex, this.y + 20 + kneey);
    ctx.fill();
    ctx.beginPath();
    ctx.moveTo (this.x + 50 + legx, this.y + 40 - legy * 2);
    ctx.lineTo (this.x + 40 + kneex, this.y + 15 - kneey);
    ctx.lineTo (this.x, this.y);
    ctx.lineTo (this.x + 40 + kneex, this.y + 20 - kneey);
    ctx.fill();
    
    ctx.beginPath();
    ctx.moveTo (this.x - 35 - legx, this.y + 70 - legy * 3);
    ctx.lineTo (this.x - 30 - kneex, this.y + 40 - legy);
    ctx.lineTo (this.x, this.y);
    ctx.lineTo (this.x - 28 - kneex, this.y + 45 - legy);
    ctx.fill();
    ctx.moveTo (this.x + 35 + legx, this.y + 70 + legy * 3);
    ctx.lineTo (this.x + 30 + kneex, this.y + 40 + legy);
    ctx.lineTo (this.x, this.y);
    ctx.lineTo (this.x + 28 + kneex, this.y + 45 + legy);
    ctx.fill();
    
    ctx.fillStyle = "rgb(196,0,196)";
    // Body + blinking eye
    ctx.beginPath();
    ctx.arc (this.x, this.y, 25, 0, Math.PI * 2, 0);
    ctx.fill();
    
    if (this.age % 64 > 3) {
      ctx.fillStyle = "rgb(255,255,255)";
      ctx.beginPath();
      ctx.arc (this.x, this.y, 12, 0, Math.PI * 2, 0);
      ctx.fill();
      
      ontarget (this.x, this.y, shipx, shipy, 5);
      
      ctx.fillStyle = "rgb(0,0,0)";
      ctx.beginPath();
      ctx.arc (this.x + shotx, this.y + shoty, 5, 0, Math.PI * 2, 0);
      ctx.fill();
    }
    else {
      ctx.fillStyle = "rgb(96,0,96)";
      ctx.fillRect (this.x - 12, this.y, 24, 2);
    }
    show_health_bar (this.hp, 125);
  }
  else if (this.type == "Crossfire") {
    // Big laser thing
    ctx.strokeStyle = "rgb(0,96,0)";
    ctx.fillStyle = "rgb(0,64,0)";
    ctx.fillRect (this.x - 30, this.y, 60, 25);
    ctx.strokeRect (this.x - 30, this.y - 1, 60, 25);
    ctx.fillRect (this.x - 5, this.y, 10, 30);
    ctx.strokeRect (this.x - 5, this.y, 10, 30);
    ctx.fillStyle = "rgb(255," + Math.floor (Math.random() * 256) + ",0)";
    ctx.beginPath();
    ctx.arc (this.x, this.y, 15, 0, Math.PI * 2, 0);
    ctx.fill();
    if (cycle_step % 4 == 0) {
      ctx.beginPath();
      ctx.arc (0, shipy, 10, 0, Math.PI * 2, 0);
      ctx.fill();
      ctx.beginPath();
      ctx.arc (479, shipy, 10, 0, Math.PI * 2, 0);
      ctx.fill();
      ctx.beginPath();
      ctx.arc (this.x, 639, 10, 0, Math.PI * 2, 0);
      ctx.fill();
    }
    show_health_bar (this.hp, 300);
  }
  else if (this.type == "Old Boss" || this.type == "New Boss") {
    ctx.fillStyle = "rgb(255,0,0)";
    ctx.fillRect (this.x - 20, this.y - 8, 40, 8);
    ctx.fillRect (this.x - 20, this.y, 8, 8);
    ctx.fillRect (this.x + 12, this.y, 8, 8);
    if (this.type == "Old Boss") show_health_bar (this.hp, 80);
    else show_health_bar (this.hp, 200);
  }
  else if (this.type == "Bomb") {
    ctx.fillStyle = "rgb(255,255,255)";
    ctx.beginPath();
    ctx.arc (this.x, this.y, 9, 0, Math.PI * 2, 0);
    ctx.fill();
    if (this.deltay > 2 || cycle_step % 2 == 0) {
      ctx.fillStyle = "rgb(192,0,0)";
      ctx.beginPath();
      ctx.arc (this.x, this.y, 8, 0, Math.PI * 2, 0);
      ctx.fill();
    }
  }
  else if (this.type == "Title") {
    ctx.fillStyle = "rgb(255,255,255)";
    ctx.font = "bold 40px Arial";
    ctx.fillText ("SUPERLUMINAL:", 75, 100);
    ctx.fillText ("WARP TWO", 120, 150);
    ctx.font = "bold 30px Arial";
    ctx.fillText ("By Mark Hall", 145, 220);
    if (cycle_step < 24) {
      ctx.font = "bold 25px Arial";
      ctx.fillStyle = "rgb(0,192,255)";
      ctx.fillText ("Press Enter to Start", 120, 300);
    }
    show_controls();
  }
  else if (this.type == "You Suck") {
    ctx.fillStyle = "rgb(255,0,0)";
    ctx.font = "bold 40px Arial";
    ctx.fillText ("GAME OVER", 120, 120);
    ctx.fillStyle = "rgb(255,255,255)";
    ctx.font = "bold 20px Arial";
    ctx.fillText ("The aliens take over Earth and", 100, 200);
    ctx.fillText ("kill everyone because you suck", 97, 225);
    if (challenge_mode != 1) {
      if (cycle_step < 24) {
        ctx.font = "bold 25px Arial";
        ctx.fillStyle = "rgb(0,192,255)";
        ctx.fillText ("Press Enter to Continue", 100, 320);
      }
      ctx.font = "bold 50px Arial";
      if (cycle_step % 8 > 4 && this.age > 300) ctx.fillStyle = "rgb(255,255,255)";
      else ctx.fillStyle = "rgb(255,0,0)";
      ctx.fillText (Math.floor ((500 - this.age) / 50) + 1, 230, 380);
    }
  }
  else {
    var visibility = 0;
    if (this.age <= 60) visibility = this.age / 60;
    else if (this.age > 120) visibility = (160 - this.age) / 60;
    else visibility = 1;
    ctx.strokeStyle = "rgba(0,0,0," + (visibility / 2) + ")";
    ctx.font = "bold 18px Arial";
    ctx.textBaseline = "top";
    ctx.fillStyle = "rgba(0,0,0," + (visibility / 2) + ")";
    ctx.fillText (this.type, this.x + 1, this.y + 1);
    ctx.fillStyle = "rgba(0,192,255," + visibility + ")";
    ctx.fillText (this.type, this.x, this.y);
  }
}

function final_boss_formation (formationtype) {
  if (formationtype == "Triangle") {
    // Big triangle shape
    for (var i = 1; i <= 6; i++) {
      Enemies[i].targetx = (i - 3.5) * 20;
      Enemies[i].targety = -45;
    }
    for (var i = 1; i <= 5; i++) {
      Enemies[6 + i].targetx = (i - 3) * 20;
      Enemies[6 + i].targety = -25;
    }
    for (var i = 1; i <= 4; i++) {
      Enemies[11 + i].targetx = (i - 2.5) * 20;
      Enemies[11 + i].targety = -5;
    }
    for (var i = 1; i <= 3; i++) {
      Enemies[15 + i].targetx = (i - 2) * 20;
      Enemies[15 + i].targety = 15;
    }
    for (var i = 1; i <= 2; i++) {
      Enemies[18 + i].targetx = (i - 1.5) * 20;
      Enemies[18 + i].targety = 35;
    }
    Enemies[21].targetx = 0;
    Enemies[21].targety = 55;
  }
  else if (formationtype == "Circles") {
    for (var i = 1; i <= 10; i++) {
      var rads = Math.PI / 10 * i * 2;
      ontarget (0, 0, 0, 0, 40, rads + (Enemies[0].age / 70));
      Enemies[i].targetx = shotx;
      Enemies[i].targety = shoty;
    }
    for (var i = 1; i <= 11; i++) {
      var rads = Math.PI / 11 * i * 2;
      ontarget (0, 0, 0, 0, 85, rads - (Enemies[0].age / 70));
      Enemies[10 + i].targetx = shotx;
      Enemies[10 + i].targety = shoty;
    }
  }
  else if (formationtype == "Squares") {
    for (var i = 1; i <= 4; i++) {
      Enemies[i].targetx = -50;
      Enemies[i].targety = (i - 2.5) * 15;
      Enemies[4 + i].targetx = 50;
      Enemies[4 + i].targety = (i - 2.5) * 15;
      Enemies[8 + i].targetx = (i - 2.5) * 15;
      Enemies[8 + i].targety = -50;
      Enemies[12 + i].targetx = (i - 2.5) * 15;
      Enemies[12 + i].targety = 50;
    }
    for (var i = 17; i <= 21; i++) {
      Enemies[i].targetx = Math.random() * 60 - 30;
      Enemies[i].targety = Math.random() * 60 - 30;
    }
  }
}

var Bullets = new Array;
var Enemies = new Array;

var dead_bullets = new Array;
var dead_baddies = new Array;
var dead_bg = new Array;

var canvas = "";
var ctx = "";
