//=========================================//
//  A S C I I R O I D S                    //
//  www.michael-hogg.co.uk/asciiroids.php  //
//  (c) 2008 Michael Hogg                  //
//  Please don't steal software            //
//=========================================//

//--------------------//
//  GLOBAL CONSTANTS  //
//--------------------//

var POWERUP_NONE   = 0;
var POWERUP_LASERS = 1;
var POWERUP_SHIELD = 2;

var WIDTH  = 63;  // The dimensions of the actual gameplay area (not including status display areas, etc)
var HEIGHT = 8;

var SHOP_PREVIEW_WIDTH = 32;

var SHOP_LASERS   = 0;   var PRICE_LASERS   = 100;    var MAX_LEVEL_LASERS   = 8;
var SHOP_SHIELD   = 1;   var PRICE_SHIELD   = 100;    var MAX_LEVEL_SHIELD   = 4;
var SHOP_ORBITERS = 2;   var PRICE_ORBITERS = 400;    var MAX_LEVEL_ORBITERS = 4;
var SHOP_MISSILES = 3;   var PRICE_MISSILES = 500;    var MAX_LEVEL_MISSILES = 4;
var SHOP_GATLING  = 4;   var PRICE_GATLING  = 1200;   var MAX_LEVEL_GATLING  = 3;

var CHARGING_RATE_INVINCIBALL = 0.04;
var CHARGING_RATE_HYPERSHIELD = 0.023;

var FIRING_DELAY_LASERS   = 6;
var FIRING_DELAY_MISSILES = 20;

var ENEMY_FIRING_DELAY = 6;

var PLAYER_RECOVERY_COUNTDOWN_DURATION = 80;
var RED_FLASH_COUNTDOWN_DURATION       = 4;

var BULLET_TYPE_SINGLE_LASER = 1;
var BULLET_TYPE_DOUBLE_LASER = 2;
var BULLET_TYPE_MISSILE      = 3;
var BULLET_TYPE_INVINCIBALL  = 4;

var ROCK_TYPE_INDESTRUCTIBLE = 0;

var ROCK_STRENGTHS = new Array(1000000,  // 0 = ROCK_TYPE_INDESTRUCTIBLE
                               1,        // 1
                               3,        // 2
                               6,        // 3
                               9,        // 4
                               12);      // 5

var ENEMY_STRENGTHS = new Array(1,    // 0
                                4,    // 1
                                9,    // 2
                                16);  // 3

var ORB_OFFSETS = new Array(new Array(1,  -3),   // 0 front
                            new Array(3,  -3),   // 1
                            new Array(5,  -2),   // 2
                            new Array(6,  -1),   // 3
                            new Array(6,   0),   // 4
                            new Array(6,   1),   // 5
                            new Array(5,   2),   // 6
                            new Array(3,   3),   // 7
                            new Array(1,   3),   // 8

                            new Array(-1,  3),   // 9  back
                            new Array(-3,  3),   // 10
                            new Array(-5,  2),   // 11
                            new Array(-6,  1),   // 12
                            new Array(-6,  0),   // 13
                            new Array(-6, -1),   // 14
                            new Array(-5, -2),   // 15
                            new Array(-3, -3),   // 16
                            new Array(-1, -3));  // 17


//--------------------//
//  GLOBAL VARIABLES  //
//--------------------//

var lasersPowerupLevel   = 0;
var shieldPowerupLevel   = 0;
var orbitersPowerupLevel = 0;
var missilesPowerupLevel = 0;
var gatlingPowerupLevel  = 0;

var lasersAutoconfigureMin = 0;
var shieldAutoconfigureMin = 0;

var invinciballCharging = 0.0;
var hypershieldCharging = 0.0;

var rocks        = new Array();
var rocksToSpawn = 0.0;
var rockLevel    = 1;

var enemies        = new Array();
var enemiesToSpawn = 0.0;
var enemyLevel     = 0;

var bullets                 = new Array();
var firingCountdownLasers   = 0;
var firingCountdownMissiles = 0;

var enemyBullets         = new Array();
var enemyFiringCountdown = 0;

var powerupType       = POWERUP_NONE;
var powerupX          = 0;
var powerupY          = 0;
var powerupSpawnDelay = 200;

var orbOffset      = 0;
var orbUpdateDelay = 0;

var playerX                 = 6;
var playerY                 = 0;
var playerRecoveryCountdown = 0;
var redFlashCountdown       = 0;
var backColour              = "#000000";

var lives           = 5;
var cash            = 0;
var score           = 0;
var totalFrameCount = 0;

var outputArrays = new Array();

var frameCount = 0;
var fps        = 15;    // target fps
var sleepTime  = 66.6;  // 15 fps = 66.6ms

var started         = false;
var paused          = false;
var cheatInvincible = false;

var inShop        = false;
var shopSelection = 0;


//---------------------//
//  GENERAL FUNCTIONS  //
//---------------------//

function ini() {
   outputArrays = new Array();
   for(var x=0; x<WIDTH; x++) {
      outputArrays[x] = new Array();
      for(var y=0; y<HEIGHT; y++) {
         outputArrays[x][y] = "";
      }
   }
   fpsUpdaterLoop();
   endlessLoop();
}

function fpsUpdaterLoop() {
   if(paused == false) {
       fps = frameCount;
       frameCount = 0;
   }
   setTimeout("fpsUpdaterLoop();", 1000);
}

function endlessLoop() {
   if(started == false || paused == true || inShop == true) {
      setTimeout("endlessLoop();", 200);
      return;
   }
   updateGameplay();
   updateDisplay();
   frameCount++;
   totalFrameCount++;
   if(fps < 15)  sleepTime -= (0.1 * (15 - fps));  // If running too slowly, then reduce the sleep time
   if(fps > 15)  sleepTime += (0.1 * (fps - 15));  // If running too fast, then increase the sleep time
   if(sleepTime < 1.0)  sleepTime = 1.0;
   setTimeout("endlessLoop();", parseInt(sleepTime));
   //if(totalFrameCount % 8 == 0)  document.getElementById('tdLoadingMusic').innerHTML = sleepTime;
}


//-------------------//
//  UPDATE GAMEPLAY  //
//-------------------//

function updateGameplay() {

   if(redFlashCountdown       > 0             )  redFlashCountdown--;
   if(playerRecoveryCountdown > 0 && lives > 0)  playerRecoveryCountdown--;

   invinciballCharging += CHARGING_RATE_INVINCIBALL;  if(invinciballCharging > 100.0)  invinciballCharging = 100.0;
   hypershieldCharging += CHARGING_RATE_HYPERSHIELD;  if(hypershieldCharging > 100.0)  hypershieldCharging = 100.0;

   if(powerupType != POWERUP_NONE) {
      if(playerRecoveryCountdown == 0 && playerX == powerupX && playerY == powerupY) {
         if(powerupType == POWERUP_LASERS)  changeLasersAndShields(1, 0);
         if(powerupType == POWERUP_SHIELD)  changeLasersAndShields(0, 1);
         powerupType = POWERUP_NONE;
      }
      powerupX--;
      if(powerupX < 0)  powerupType = POWERUP_NONE;
   }

   if(orbUpdateDelay > 0)  orbUpdateDelay--;
   if(orbUpdateDelay <= 0) {
      orbOffset++;
      if(orbOffset >= 9)  orbOffset = 0;
      orbUpdateDelay = 3;
   }

   for(var j = bullets.length - 1; j >= 0; j--) {
      bullets[j].x++;
      if(bullets[j].x >= WIDTH)  bullets.splice(j,1);
   }

   for(var j = enemyBullets.length - 1; j >= 0; j--) {
      var thisEnemyBullet = enemyBullets[j];
      if(cheatInvincible == false && thisEnemyBullet.y == playerY && hitTest(thisEnemyBullet.x, playerX) == true) {
         if(playerRecoveryCountdown == 0 && shieldPowerupLevel > 0) {
            changeLasersAndShields(-1, -1);
         } else if(playerRecoveryCountdown == 0 && hypershieldCharging == 100.0) {
            hypershieldCharging = 0.0;
         } else {
            shipDestroyed();
         }
         redFlashCountdown = RED_FLASH_COUNTDOWN_DURATION;
         enemyBullets.splice(j,1);
      } else if(checkForOrbAtCoords(thisEnemyBullet.x, thisEnemyBullet.y) == true || checkForOrbAtCoords(thisEnemyBullet.x + 1, thisEnemyBullet.y) == true) {
         enemyBullets.splice(j,1);
      } else {
         thisEnemyBullet.x -= 2;
         if(thisEnemyBullet.x < 0)  enemyBullets.splice(j,1);
      }
   }

   for(var i = rocks.length - 1; i >= 0; i--) {
      updateRock(i);
   }

   for(var i = enemies.length - 1; i >= 0; i--) {
      updateEnemy(i);
   }

   powerupSpawnDelay--;
   if(powerupSpawnDelay <= 0) {
      powerupX    = WIDTH - 1;
      powerupY    = rand(0, HEIGHT - 1);
      powerupType = rand(POWERUP_LASERS, POWERUP_SHIELD);
      powerupSpawnDelay = rand(200,300);
   }

   rockLevel = 1 + parseInt(totalFrameCount / 1500.0);
   if(rockLevel > 5)  rockLevel = 5;

   rocksToSpawn += ((totalFrameCount / 20000.0) + 0.2);
   while(rocksToSpawn >= 1.0) {
      rocks.push(new Object());
      var newRock = rocks[rocks.length - 1];
      newRock.x      = WIDTH - 1;
      newRock.y      = rand(0, HEIGHT - 1);
      newRock.type   = (Math.random() < 0.33) ? ROCK_TYPE_INDESTRUCTIBLE : rand(1, rockLevel);
      newRock.health = ROCK_STRENGTHS[newRock.type];
      rocksToSpawn -= 1.0;
   }

   enemyLevel = parseInt(totalFrameCount / 2500.0);
   if(enemyLevel > 3)  enemyLevel = 3;

   enemiesToSpawn += ((totalFrameCount / 4000000.0) + 0.001);
   while(enemiesToSpawn >= 1.0) {
      var newType = rand(0, enemyLevel);
      var minY    = 0;           if(newType >= 2) minY++;
      var maxY    = HEIGHT - 1;  if(newType >= 1) maxY--;  if(newType >= 3) maxY--;
      enemies.push(new Object());
      var newEnemy = enemies[enemies.length - 1];
      newEnemy.x       = WIDTH - 1;
      newEnemy.y       = rand(minY, maxY);
      newEnemy.type    = newType;
      newEnemy.health  = ENEMY_STRENGTHS[newType];
      newEnemy.spinner = 0;
      enemiesToSpawn -= 1.0;
   }

   if(playerRecoveryCountdown == 0) {
      firingCountdownLasers--;
      if(firingCountdownLasers <= 0) {
         firingCountdownLasers = FIRING_DELAY_LASERS - gatlingPowerupLevel;
         if(lasersPowerupLevel % 2 == 0)                        { spawnNewBullet(playerX,     playerY,     BULLET_TYPE_SINGLE_LASER);
         }
         if(lasersPowerupLevel >= 1 && lasersPowerupLevel <= 2) { spawnNewBullet(playerX,     playerY - 1, BULLET_TYPE_SINGLE_LASER);
                                                                  spawnNewBullet(playerX,     playerY + 1, BULLET_TYPE_SINGLE_LASER);
         }
         if(lasersPowerupLevel >= 3)                            { spawnNewBullet(playerX,     playerY - 1, BULLET_TYPE_DOUBLE_LASER);
                                                                  spawnNewBullet(playerX,     playerY + 1, BULLET_TYPE_DOUBLE_LASER);
         }
         if(lasersPowerupLevel >= 5 && lasersPowerupLevel <= 6) { spawnNewBullet(playerX + 1, playerY - 2, BULLET_TYPE_SINGLE_LASER);
                                                                  spawnNewBullet(playerX + 1, playerY + 2, BULLET_TYPE_SINGLE_LASER);
         }
         if(lasersPowerupLevel >= 7)                            { spawnNewBullet(playerX + 1, playerY - 2, BULLET_TYPE_DOUBLE_LASER);
                                                                  spawnNewBullet(playerX + 1, playerY + 2, BULLET_TYPE_DOUBLE_LASER);
         }
      }
      firingCountdownMissiles--;
      if(firingCountdownMissiles <= 0) {
         firingCountdownMissiles = FIRING_DELAY_MISSILES;
         if(missilesPowerupLevel >= 1)  spawnNewBullet(playerX - 3, playerY - 1, BULLET_TYPE_MISSILE);
         if(missilesPowerupLevel >= 2)  spawnNewBullet(playerX - 3, playerY + 1, BULLET_TYPE_MISSILE);
         if(missilesPowerupLevel >= 3)  spawnNewBullet(playerX - 2, playerY - 2, BULLET_TYPE_MISSILE);
         if(missilesPowerupLevel >= 4)  spawnNewBullet(playerX - 2, playerY + 2, BULLET_TYPE_MISSILE);
      }
   }

   enemyFiringCountdown--;
   if(enemyFiringCountdown <= 0) {
      enemyFiringCountdown = ENEMY_FIRING_DELAY;
      for(var i=0; i<enemies.length; i++) {
         spawnNewEnemyBullets(enemies[i]);
      }
   }

}

function updateRock(i) {
   var thisRock = rocks[i];
   if(cheatInvincible == false && thisRock.x == playerX && thisRock.y == playerY) {
      if(playerRecoveryCountdown == 0 && thisRock.type != ROCK_TYPE_INDESTRUCTIBLE && shieldPowerupLevel > 0) {
         changeLasersAndShields(-1, -1);
      } else if(playerRecoveryCountdown == 0 && hypershieldCharging == 100.0) {
         hypershieldCharging = 0.0;
      } else {
         shipDestroyed();
      }
      redFlashCountdown = RED_FLASH_COUNTDOWN_DURATION;
      rocks.splice(i,1);  return;
   }
   if(checkForOrbAtCoords(thisRock.x, thisRock.y) == true) {
      rocks.splice(i,1);  return;
   }
   for(var j = bullets.length - 1; j >= 0; j--) {
      if(bullets[j].y == thisRock.y && hitTest(bullets[j].x, thisRock.x) == true) {
         switch(bullets[j].type) {
            case BULLET_TYPE_SINGLE_LASER:
            case BULLET_TYPE_DOUBLE_LASER:
               thisRock.health -= bullets[j].type;
               bullets.splice(j,1);
               if(thisRock.health <= 0) {
                  increaseCashAndScore( ROCK_STRENGTHS[thisRock.type] );
                  rocks.splice(i,1);  return;
               }
               break;
            case BULLET_TYPE_MISSILE:
               bullets.splice(j,1);
               if(thisRock.type != ROCK_TYPE_INDESTRUCTIBLE) {
                  increaseCashAndScore( ROCK_STRENGTHS[thisRock.type] );
                  rocks.splice(i,1);  return;
               }
               break;
            case BULLET_TYPE_INVINCIBALL:
               if(thisRock.type != ROCK_TYPE_INDESTRUCTIBLE) {
                  increaseCashAndScore( ROCK_STRENGTHS[thisRock.type] );
               }
               rocks.splice(i,1);  return;
               break;
         }
      }
   }
   thisRock.x--;
   if(thisRock.x < 0)  rocks.splice(i,1);
}

function updateEnemy(i) {
   var thisEnemy = enemies[i];
   if(cheatInvincible == false && hitEnemyTest(playerX, playerY, thisEnemy) == true) {
      if(playerRecoveryCountdown == 0 && hypershieldCharging == 100.0)  hypershieldCharging = 0.0;
      else                                                              shipDestroyed();
      redFlashCountdown = RED_FLASH_COUNTDOWN_DURATION;
      enemies.splice(i,1);  return;
   }
   if(checkForOrbAtCoords(thisEnemy.x, thisEnemy.y) == true) {
      enemies.splice(i,1);  return;
   }
   for(var j = bullets.length - 1; j >= 0; j--) {
      if(hitEnemyTest(bullets[j].x, bullets[j].y, thisEnemy) == true) {
         switch(bullets[j].type) {
            case BULLET_TYPE_SINGLE_LASER:
            case BULLET_TYPE_DOUBLE_LASER:
               thisEnemy.health -= bullets[j].type;
               bullets.splice(j,1);
               if(thisEnemy.health <= 0) {
                  increaseCashAndScore( ENEMY_STRENGTHS[thisEnemy.type] );
                  enemies.splice(i,1);  return;
               }
               break;
            case BULLET_TYPE_MISSILE:
               bullets.splice(j,1);
               // Fall through to next case
            case BULLET_TYPE_INVINCIBALL:
               increaseCashAndScore( ENEMY_STRENGTHS[thisEnemy.type] );
               enemies.splice(i,1);  return;
               break;
         }
      }
   }
   thisEnemy.spinner++;  if(thisEnemy.spinner >= 8)  thisEnemy.spinner = 0;
   thisEnemy.x--;        if(thisEnemy.x       < -4)  enemies.splice(i,1);
}

function hitTest(bulletX, rockX) {
   if(bulletX == rockX)      return true;
   if(bulletX - rockX == 1)  return true;
   return false;
}

function hitEnemyTest(pointX, pointY, thisEnemy) {
   var enemyX = thisEnemy.x;
   var enemyY = thisEnemy.y;
   switch(thisEnemy.type) {
      case 0:  if(pointX >= enemyX && pointX <= enemyX + 2 && pointY == enemyY                            )  return true;  break;
      case 1:  if(pointX >= enemyX && pointX <= enemyX + 2 && pointY >= enemyY     && pointY <= enemyY + 1)  return true;  break;
      case 2:  if(pointX >= enemyX && pointX <= enemyX + 4 && pointY >= enemyY - 1 && pointY <= enemyY + 1)  return true;  break;
      case 3:  if(pointX >= enemyX && pointX <= enemyX + 4 && pointY >= enemyY - 1 && pointY <= enemyY + 2)  return true;  break;
   }
   return false;
}

function spawnNewBullet(x, y, type) {
   if(y < 0 || y >= HEIGHT)  return;
   bullets.push(new Object());
   var newBullet = bullets[bullets.length - 1];
   newBullet.x    = x;
   newBullet.y    = y;
   newBullet.type = type;
}

function spawnNewEnemyBullets(thisEnemy) {
   var x = thisEnemy.x;
   var y = thisEnemy.y;
   switch(thisEnemy.type) {
      case 0:
         spawnNewEnemyBullet(x,   y  );
         break;
      case 1:
         spawnNewEnemyBullet(x,   y  );
         spawnNewEnemyBullet(x,   y+1);
         break;
      case 2:
         spawnNewEnemyBullet(x,   y-1);
         spawnNewEnemyBullet(x+1, y  );
         spawnNewEnemyBullet(x,   y+1);
         break;
      case 3:
         spawnNewEnemyBullet(x,   y-1);
         spawnNewEnemyBullet(x+1, y  );
         spawnNewEnemyBullet(x+1, y+1);
         spawnNewEnemyBullet(x,   y+2);
         break;
   }
}

function spawnNewEnemyBullet(x, y) {
   if(y < 0 || y >= HEIGHT || x < 0 || x >= WIDTH)  return;
   enemyBullets.push(new Object());
   var newEnemyBullet = enemyBullets[enemyBullets.length - 1];
   newEnemyBullet.x = x;
   newEnemyBullet.y = y;
}


function checkForOrbAtCoords(x, y) {
   if(orbitersPowerupLevel < 1)  return false;
   if(orbitersPowerupLevel >= 1) { if(x == getOrbX(orbOffset + 0)  && y == getOrbY(orbOffset + 0,  playerY))  return true;
                                   if(x == getOrbX(orbOffset + 9)  && y == getOrbY(orbOffset + 9,  playerY))  return true;
   }
   if(orbitersPowerupLevel >= 2) { if(x == getOrbX(orbOffset + 1)  && y == getOrbY(orbOffset + 1,  playerY))  return true;
                                   if(x == getOrbX(orbOffset + 10) && y == getOrbY(orbOffset + 10, playerY))  return true;
   }
   if(orbitersPowerupLevel >= 3) { if(x == getOrbX(orbOffset + 2)  && y == getOrbY(orbOffset + 2,  playerY))  return true;
                                   if(x == getOrbX(orbOffset + 11) && y == getOrbY(orbOffset + 11, playerY))  return true;
   }
   if(orbitersPowerupLevel >= 4) { if(x == getOrbX(orbOffset + 3)  && y == getOrbY(orbOffset + 3,  playerY))  return true;
                                   if(x == getOrbX(orbOffset + 12) && y == getOrbY(orbOffset + 12, playerY))  return true;
   }
   return false;
}

function getOrbX(thisOrbOffset             ) { return ORB_OFFSETS[thisOrbOffset % 18][0] + playerX;     }
function getOrbY(thisOrbOffset, thisPlayerY) { return ORB_OFFSETS[thisOrbOffset % 18][1] + thisPlayerY; }

function shipDestroyed() {
   if(playerRecoveryCountdown == 0) {
      lives--;
      lasersPowerupLevel = 0;
      shieldPowerupLevel = 0;
   }
   playerRecoveryCountdown = PLAYER_RECOVERY_COUNTDOWN_DURATION;
   redFlashCountdown       = RED_FLASH_COUNTDOWN_DURATION;
   autoconfigurePowerups();
}

function changeLasersAndShields(deltaLasers, deltaShields) {
    if(deltaLasers == 1) {
        lasersPowerupLevel++;
        if(lasersPowerupLevel > MAX_LEVEL_LASERS)  { lasersPowerupLevel = MAX_LEVEL_LASERS;  cash += PRICE_LASERS; }
    }
    if(deltaLasers == -1) {
        lasersPowerupLevel--;
        if(lasersPowerupLevel < 0)  lasersPowerupLevel = 0;
    }
    if(deltaShields == 1) {
        shieldPowerupLevel++;
        if(shieldPowerupLevel > MAX_LEVEL_SHIELD)  { shieldPowerupLevel = MAX_LEVEL_SHIELD;  cash += PRICE_SHIELD; }
    }
    if(deltaShields == -1) {
        shieldPowerupLevel--;
        if(shieldPowerupLevel < 0)  shieldPowerupLevel = 0;
    }
    autoconfigurePowerups();
}

function autoconfigurePowerups() {

   // The player sets the minimum required levels of lasers and shields in the powerup shop (lasersAutoconfigureMin and shieldAutoconfigureMin).
   // If the laser/shield levels fall below these minimums, then buy more lasers/shields to reach the minimum levels.
   // If there's not enough cash, then sell one or more missile powerups to obtain cash.
   // If there aren't any missile powerups, then sell one or more gatling powerups to obtain cash.
   // If there aren't any gatling powerups, then sell one or more laser powerups to obtain cash (only if there's a shield shortage).
   // Otherwise, the laser/shield shortage can't be resolved.

   var shieldShortage = shieldAutoconfigureMin - shieldPowerupLevel;
   var nothingToSell  = false;

   while(shieldShortage > 0) {  // If there's a shield shortage...
      nothingToSell = false;
      while(cash < PRICE_SHIELD) {  // If there's not enough cash to buy a shield...
         if(    missilesPowerupLevel > 0) { missilesPowerupLevel--; cash += PRICE_MISSILES; }  // If there's a missile, then sell it
         else if(gatlingPowerupLevel > 0) { gatlingPowerupLevel--;  cash += PRICE_GATLING;  }  // Otherwise, if there's a gatling, then sell it
         else if( lasersPowerupLevel > 0) { lasersPowerupLevel--;   cash += PRICE_LASERS;   }  // Otherwise, if there's a laser, then sell it
         else                             { nothingToSell = true;   break;                  }  // Otherwise, nothing to sell
      }
      if(nothingToSell == true) {  // If not enough cash to buy a shield, and nothing to sell to raise cash, then abort
         break;
      } else {                     // Otherwise, buy a shield
         shieldPowerupLevel++;
         cash -= PRICE_SHIELD;
         shieldShortage--;
      }
   }

   var lasersShortage = lasersAutoconfigureMin - lasersPowerupLevel;

   while(lasersShortage > 0) {  // If there's a laser shortage...
      nothingToSell = false;
      while(cash < PRICE_LASERS) {  // If there's not enough cash to buy a laser...
         if(    missilesPowerupLevel > 0) { missilesPowerupLevel--; cash += PRICE_MISSILES; }  // If there's a missile, then sell it
         else if(gatlingPowerupLevel > 0) { gatlingPowerupLevel--;  cash += PRICE_GATLING;  }  // Otherwise, if there's a gatling, then sell it
         else                             { nothingToSell = true;   break;                  }  // Otherwise, nothing to sell
      }
      if(nothingToSell == true) {  // If not enough cash to buy a laser, and nothing to sell to raise cash, then abort
         break;
      } else {                     // Otherwise, buy a laser
         lasersPowerupLevel++;
         cash -= PRICE_LASERS;
         lasersShortage--;
      }
   }

}

function increaseCashAndScore(thisAmount) {
   cash  += thisAmount;
   score += thisAmount;
}

function rand(minLimit, maxLimit) {
   return parseInt(minLimit + (Math.random() * (1 + maxLimit - minLimit)));
}


//------------------//
//  UPDATE DISPLAY  //
//------------------//

function updateDisplay() {

   for(var x=0; x<WIDTH; x++) {
      for(var y=0; y<HEIGHT; y++) {
         outputArrays[x][y] = " ";
      }
   }

   if(inShop == true) {

      var laserStepSize = FIRING_DELAY_LASERS - gatlingPowerupLevel;

      if(lasersPowerupLevel % 2 == 0)                        {  for(var x = playerX;     x < WIDTH; x += laserStepSize)  outputArrays[x][4] = "<span class=mycyan>-</span>";
      }
      if(lasersPowerupLevel >= 1 && lasersPowerupLevel <= 2) {  for(var x = playerX;     x < WIDTH; x += laserStepSize)  outputArrays[x][3] = "<span class=mycyan>-</span>";
                                                                for(var x = playerX;     x < WIDTH; x += laserStepSize)  outputArrays[x][5] = "<span class=mycyan>-</span>";
      }
      if(lasersPowerupLevel >= 3)                            {  for(var x = playerX;     x < WIDTH; x += laserStepSize)  outputArrays[x][3] = "<span class=mycyan>=</span>";
                                                                for(var x = playerX;     x < WIDTH; x += laserStepSize)  outputArrays[x][5] = "<span class=mycyan>=</span>";
      }
      if(lasersPowerupLevel >= 5 && lasersPowerupLevel <= 6) {  for(var x = playerX + 1; x < WIDTH; x += laserStepSize)  outputArrays[x][2] = "<span class=mycyan>-</span>";
                                                                for(var x = playerX + 1; x < WIDTH; x += laserStepSize)  outputArrays[x][6] = "<span class=mycyan>-</span>";
      }
      if(lasersPowerupLevel >= 7)                            {  for(var x = playerX + 1; x < WIDTH; x += laserStepSize)  outputArrays[x][2] = "<span class=mycyan>=</span>";
                                                                for(var x = playerX + 1; x < WIDTH; x += laserStepSize)  outputArrays[x][6] = "<span class=mycyan>=</span>";
      }

      if(missilesPowerupLevel >= 1)  for(var x = playerX - 3; x < WIDTH; x += FIRING_DELAY_MISSILES)  outputArrays[x][3] = "<span class=myblue>x</span>";
      if(missilesPowerupLevel >= 2)  for(var x = playerX - 3; x < WIDTH; x += FIRING_DELAY_MISSILES)  outputArrays[x][5] = "<span class=myblue>x</span>";
      if(missilesPowerupLevel >= 3)  for(var x = playerX - 2; x < WIDTH; x += FIRING_DELAY_MISSILES)  outputArrays[x][2] = "<span class=myblue>x</span>";
      if(missilesPowerupLevel >= 4)  for(var x = playerX - 2; x < WIDTH; x += FIRING_DELAY_MISSILES)  outputArrays[x][6] = "<span class=myblue>x</span>";

      drawShipPowerups(4);

      if(hypershieldCharging == 100.0)  outputArrays[playerX][4] = "<span class=mygreen>&gt;</span>";
      else                              outputArrays[playerX][4] = "<span class=mywhite>&gt;</span>";

   } else {

      var thisBullet;
      for(var j=0; j<bullets.length; j++) {
         thisBullet = bullets[j];
         switch(thisBullet.type) {
            case BULLET_TYPE_SINGLE_LASER:  outputArrays[thisBullet.x][thisBullet.y] = "<span class=mycyan>-</span>";        break;
            case BULLET_TYPE_DOUBLE_LASER:  outputArrays[thisBullet.x][thisBullet.y] = "<span class=mycyan>=</span>";        break;
            case BULLET_TYPE_MISSILE:       outputArrays[thisBullet.x][thisBullet.y] = "<span class=myblue>x</span>";        break;
            case BULLET_TYPE_INVINCIBALL:   outputArrays[thisBullet.x][thisBullet.y] = "<span class=mygreen>\u00a9</span>";  break;
         }
      }

      var thisEnemyBullet;
      for(var j=0; j<enemyBullets.length; j++) {
         thisEnemyBullet = enemyBullets[j];
         outputArrays[thisEnemyBullet.x][thisEnemyBullet.y] = "<span class=mylime>~</span>";
      }

      if(powerupType == POWERUP_LASERS)  outputArrays[powerupX][powerupY] = "<span class=mycyan>@</span>";
      if(powerupType == POWERUP_SHIELD)  outputArrays[powerupX][powerupY] = "<span class=myyellow>@</span>";

      var thisRock;
      for(var i=0; i<rocks.length; i++) {
         thisRock = rocks[i];
         switch(thisRock.type) {
            case ROCK_TYPE_INDESTRUCTIBLE:  outputArrays[thisRock.x][thisRock.y] = "<span class=mywhite>O</span>";      break;
            case 1:                         outputArrays[thisRock.x][thisRock.y] = "<span class=myrocka>:</span>";      break;
            case 2:                         outputArrays[thisRock.x][thisRock.y] = "<span class=myrockb>*</span>";      break;
            case 3:                         outputArrays[thisRock.x][thisRock.y] = "<span class=myrockc>&amp;</span>";  break;
            case 4:                         outputArrays[thisRock.x][thisRock.y] = "<span class=myrockd>#</span>";      break;
            case 5:                         outputArrays[thisRock.x][thisRock.y] = "<span class=myrocke>W</span>";      break;
         }
      }

      for(var i=0; i<enemies.length; i++) {
         drawEnemy(enemies[i]);
      }

      drawShipPowerups(playerY);

      if(playerRecoveryCountdown > 0)        outputArrays[playerX][playerY] = "<span class=myred>X</span>";
      else if(cheatInvincible == true)       outputArrays[playerX][playerY] = "<span class=mygreen>&gt;</span>";
      else if(hypershieldCharging == 100.0)  outputArrays[playerX][playerY] = "<span class=mygreen>&gt;</span>";
      else                                   outputArrays[playerX][playerY] = "<span class=mywhite>&gt;</span>";

   }

   if(redFlashCountdown > 0) {
       if(backColour != "#ff0000")  { backColour = "#ff0000";  document.getElementById('tdMain').style.backgroundColor = backColour; }
   } else {
       if(backColour != "#000000")  { backColour = "#000000";  document.getElementById('tdMain').style.backgroundColor = backColour; }
   }

   var sOut = "";

   if(inShop == true) {

      var totalCash = cash + (lasersPowerupLevel   * PRICE_LASERS)   + (shieldPowerupLevel   * PRICE_SHIELD) + (gatlingPowerupLevel * PRICE_GATLING)
                           + (orbitersPowerupLevel * PRICE_ORBITERS) + (missilesPowerupLevel * PRICE_MISSILES);

      // Row 0
      sOut += "<span class=mywhite>~~~~~  P O W E R U P   S H O P  ~~~~~</span>       <span class=mywhite>|  Preview of powerups</span><br>";

      // Row 1
      sOut += "<span class=myyellow>Q</span> <span class=mygrey>= up</span>       <span class=myyellow>K</span> <span class=mygrey>= sell</span>     ";
      sOut += "<span class=myyellow>N</span> <span class=mygrey>= decrease min</span>  <span class=mywhite>|</span>  ";
      for(var x=0; x<SHOP_PREVIEW_WIDTH; x++)  sOut += outputArrays[x][1];
      sOut += "<br>";

      // Row 2
      sOut += "<span class=myyellow>A</span> <span class=mygrey>= down</span>     <span class=myyellow>L</span> <span class=mygrey>= buy</span>      ";
      sOut += "<span class=myyellow>M</span> <span class=mygrey>= increase min</span>  <span class=mywhite>|</span>  ";
      for(var x=0; x<SHOP_PREVIEW_WIDTH; x++)  sOut += outputArrays[x][2];
      sOut += "<br>";

      // Row 3
      if(shopSelection == SHOP_LASERS)  sOut += "<span class=myorange>--&gt;  ";  else sOut += "     <span class=mywhite>";
      sOut += "Lasers</span>      <span class=mycyan>" + lasersPowerupLevel + "/" + MAX_LEVEL_LASERS + "    </span><span class=";
      if(lasersPowerupLevel >= MAX_LEVEL_LASERS)  sOut += "mygrey";  else if(cash >= PRICE_LASERS)  sOut += "mygreen";  else sOut += "myred";
      sOut += ">$" + alignLeft(PRICE_LASERS) + "</span>    <span class=mymagenta>" + alignLeft(lasersAutoconfigureMin) + "</span>     <span class=mywhite>|</span>  ";
      for(var x=0; x<SHOP_PREVIEW_WIDTH; x++)  sOut += outputArrays[x][3];
      sOut += "<br>";

      // Row 4
      if(shopSelection == SHOP_SHIELD)  sOut += "<span class=myorange>--&gt;  ";  else sOut += "     <span class=mywhite>";
      sOut += "Shield</span>      <span class=mycyan>" + shieldPowerupLevel + "/" + MAX_LEVEL_SHIELD + "    </span><span class=";
      if(shieldPowerupLevel >= MAX_LEVEL_SHIELD)  sOut += "mygrey";  else if(cash >= PRICE_SHIELD)  sOut += "mygreen";  else sOut += "myred";
      sOut += ">$" + alignLeft(PRICE_SHIELD) + "</span>    <span class=mymagenta>" + alignLeft(shieldAutoconfigureMin) + "</span>     <span class=mywhite>|</span>  ";
      for(var x=0; x<SHOP_PREVIEW_WIDTH; x++)  sOut += outputArrays[x][4];
      sOut += "<br>";

      // Row 5
      if(shopSelection == SHOP_ORBITERS)  sOut += "<span class=myorange>--&gt;  ";  else sOut += "     <span class=mywhite>";
      sOut += "Orbiters</span>    <span class=mycyan>" + orbitersPowerupLevel + "/" + MAX_LEVEL_ORBITERS + "    </span><span class=";
      if(orbitersPowerupLevel >= MAX_LEVEL_ORBITERS)  sOut += "mygrey";  else if(cash >= PRICE_ORBITERS)  sOut += "mygreen";  else sOut += "myred";
      sOut += ">$" + alignLeft(PRICE_ORBITERS) + "</span><span class=mywhite>    ^         |</span>  ";
      for(var x=0; x<SHOP_PREVIEW_WIDTH; x++)  sOut += outputArrays[x][5];
      sOut += "<br>";   

      // Row 6
      if(shopSelection == SHOP_MISSILES)  sOut += "<span class=myorange>--&gt;  ";  else sOut += "     <span class=mywhite>";
      sOut += "Missiles</span>    <span class=mycyan>" + missilesPowerupLevel + "/" + MAX_LEVEL_MISSILES + "    </span><span class=";
      if(missilesPowerupLevel >= MAX_LEVEL_MISSILES)  sOut += "mygrey";  else if(cash >= PRICE_MISSILES)  sOut += "mygreen";  else sOut += "myred";
      sOut += ">$" + alignLeft(PRICE_MISSILES) + "</span><span class=mywhite>   Min        |</span>  ";
      for(var x=0; x<SHOP_PREVIEW_WIDTH; x++)  sOut += outputArrays[x][6];
      sOut += "<br>";

      // Row 7
      if(shopSelection == SHOP_GATLING)  sOut += "<span class=myorange>--&gt;  ";  else sOut += "     <span class=mywhite>";
      sOut += "Gatling</span>     <span class=mycyan>" + gatlingPowerupLevel + "/" + MAX_LEVEL_GATLING + "    </span><span class=";
      if(gatlingPowerupLevel >= MAX_LEVEL_GATLING)  sOut += "mygrey";  else if(cash >= PRICE_GATLING)  sOut += "mygreen";  else sOut += "myred";
      sOut += ">$" + alignLeft(PRICE_GATLING) + "</span><span class=mywhite>              |</span>  ";
      for(var x=0; x<SHOP_PREVIEW_WIDTH; x++)  sOut += outputArrays[x][7];
      sOut += "<br>";

      // Bottom
      sOut += "<span class=mywhite>Total:</span> <span class=";
      if(totalCash < PRICE_LASERS)  sOut += "myred";  else sOut += "mygreen";
      sOut += ">$" + alignLeft(totalCash) + "</span>";
      sOut += "  <span class=mywhite>Spare:</span> <span class=";
      if(cash < PRICE_LASERS)  sOut += "myred";  else sOut += "mygreen";
      sOut += ">$" + alignLeft(cash) + "</span>                <span class=mywhite>|</span>  ";

   } else {

      for(var y=0; y<HEIGHT; y++) {
         switch(y) {
            case 0:  sOut += "<span class=mywhite >   Cash: $" + alignLeft(cash)                + "</span>";   break;
            case 1:  sOut += "<span class=mywhite >  Lives: " + alignLeft(lives)                + " </span>";  break;
            case 2:  sOut += "<span class=mycyan  > Lasers: " + alignLeft(lasersPowerupLevel)   + " </span>";  break;
            case 3:  sOut += "<span class=myyellow>Shields: " + alignLeft(shieldPowerupLevel)   + " </span>";  break;
            case 4:  sOut += "<span class=myyellow>Orbiter: " + alignLeft(orbitersPowerupLevel) + " </span>";  break;
            case 5:  sOut += "<span class=myblue  >Missile: " + alignLeft(missilesPowerupLevel) + " </span>";  break;
            case 6:  sOut += "<span class=mycyan  >Gatling: " + alignLeft(gatlingPowerupLevel)  + " </span>";  break;
            case 7:  sOut += "<span class=mywhite >  Score: " + alignLeft(score)                + " </span>";  break;
         }
         sOut += "<span class=mywhite>|</span>";
         for(var x=0; x<WIDTH; x++) {
            sOut += outputArrays[x][y];
         }
         sOut += "<br>";
      }

      sOut += "<span class=mywhite>    FPS: " + alignLeft(fps) + " |</span>";
      if(lives <= 0)                        sOut += "           <span class=mypink>~~~~~   G  A  M  E     O  V  E  R   ~~~~~</span>";
      else if(paused == true)               sOut += "               <span class=mycyan>~~~~~   P  A  U  S  E  D   ~~~~~</span>";
      else if(playerRecoveryCountdown > 0)  sOut += "  <span class=mypink>Repairing damage:  </span>" + drawDamageRepairBar();
      else                                  sOut += "  <span class=mywhite>Invinciball: </span>" + drawProgressBar(invinciballCharging) + "    <span class=mywhite>Hypershield: </span>" + drawProgressBar(hypershieldCharging);

   }

   document.getElementById('tdMain').innerHTML = "<pre style=\"font-family: 'Courier New', Courier; font-size: 12pt;\">" + sOut + "</pre>";
}

function drawShipPowerups(thisPlayerY) {

   if(invinciballCharging == 100.0)  outputArrays[playerX - 2][thisPlayerY] = "<span class=mygreen>\u00a9</span>";

   if(shieldPowerupLevel >= 1)  outputArrays[playerX + 1][thisPlayerY] = "<span class=myyellow>)</span>";
   if(shieldPowerupLevel >= 2)  outputArrays[playerX + 1][thisPlayerY] = "<span class=myyellow>D</span>";
   if(shieldPowerupLevel >= 3)  outputArrays[playerX + 2][thisPlayerY] = "<span class=myyellow>)</span>";
   if(shieldPowerupLevel >= 4)  outputArrays[playerX + 2][thisPlayerY] = "<span class=myyellow>D</span>";

   if(orbitersPowerupLevel >= 1) { outputArrays[ getOrbX(orbOffset + 0)  ][ getOrbY(orbOffset + 0,  thisPlayerY) ] = "<span class=myyellow>\u00a9</span>";
                                   outputArrays[ getOrbX(orbOffset + 9)  ][ getOrbY(orbOffset + 9,  thisPlayerY) ] = "<span class=myyellow>\u00a9</span>";
   }
   if(orbitersPowerupLevel >= 2) { outputArrays[ getOrbX(orbOffset + 1)  ][ getOrbY(orbOffset + 1,  thisPlayerY) ] = "<span class=myyellow>\u00a9</span>";
                                   outputArrays[ getOrbX(orbOffset + 10) ][ getOrbY(orbOffset + 10, thisPlayerY) ] = "<span class=myyellow>\u00a9</span>";
   }
   if(orbitersPowerupLevel >= 3) { outputArrays[ getOrbX(orbOffset + 2)  ][ getOrbY(orbOffset + 2,  thisPlayerY) ] = "<span class=myyellow>\u00a9</span>";
                                   outputArrays[ getOrbX(orbOffset + 11) ][ getOrbY(orbOffset + 11, thisPlayerY) ] = "<span class=myyellow>\u00a9</span>";
   }
   if(orbitersPowerupLevel >= 4) { outputArrays[ getOrbX(orbOffset + 3)  ][ getOrbY(orbOffset + 3,  thisPlayerY) ] = "<span class=myyellow>\u00a9</span>";
                                   outputArrays[ getOrbX(orbOffset + 12) ][ getOrbY(orbOffset + 12, thisPlayerY) ] = "<span class=myyellow>\u00a9</span>";
   }

   if(lasersPowerupLevel >= 1) { outputArrays[playerX][thisPlayerY - 1] = "<span class=mycyan>o</span>";
                                 outputArrays[playerX][thisPlayerY + 1] = "<span class=mycyan>o</span>";
   }
   if(lasersPowerupLevel >= 5) { outputArrays[playerX + 1][thisPlayerY - 2] = "<span class=mycyan>o</span>";
                                 outputArrays[playerX + 1][thisPlayerY + 2] = "<span class=mycyan>o</span>";
   }

   if(missilesPowerupLevel >= 1)  outputArrays[playerX - 3][thisPlayerY - 1] = "<span class=myblue>[</span>";
   if(missilesPowerupLevel >= 2)  outputArrays[playerX - 3][thisPlayerY + 1] = "<span class=myblue>[</span>";
   if(missilesPowerupLevel >= 3)  outputArrays[playerX - 2][thisPlayerY - 2] = "<span class=myblue>[</span>";
   if(missilesPowerupLevel >= 4)  outputArrays[playerX - 2][thisPlayerY + 2] = "<span class=myblue>[</span>";

}

function drawEnemy(thisEnemy) {

    var x = thisEnemy.x;
    var y = thisEnemy.y;

    var thisSpinner = " ";

    switch(thisEnemy.spinner) {
       case 0:  case 1:  thisSpinner = "-";   break;
       case 2:  case 3:  thisSpinner = "/";  break;
       case 4:  case 5:  thisSpinner = "|";   break;
       case 6:  case 7:  thisSpinner = "\\";   break;
    }

    var X_IS_VISIBLE  = (x     >= 0 && x     < WIDTH);
    var X1_IS_VISIBLE = (x + 1 >= 0 && x + 1 < WIDTH);
    var X2_IS_VISIBLE = (x + 2 >= 0 && x + 2 < WIDTH);
    var X3_IS_VISIBLE = (x + 3 >= 0 && x + 3 < WIDTH);
    var X4_IS_VISIBLE = (x + 4 >= 0 && x + 4 < WIDTH);
    
    var YM1_IS_VISIBLE = (y - 1 >= 0 && y - 1 < HEIGHT);
    var Y_IS_VISIBLE   = (y     >= 0 && y     < HEIGHT);
    var Y1_IS_VISIBLE  = (y + 1 >= 0 && y + 1 < HEIGHT);
    var Y2_IS_VISIBLE  = (y + 2 >= 0 && y + 2 < HEIGHT);

    switch(thisEnemy.type) {

        case 0:
            if(X_IS_VISIBLE) {
                if(Y_IS_VISIBLE)  outputArrays[x  ][y  ] = "<span class=mylime>&lt;</span>";
            }
            if(X1_IS_VISIBLE) {
                if(Y_IS_VISIBLE)  outputArrays[x+1][y  ] = "<span class=mylime>" + thisSpinner + "</span>";
            }
            if(X2_IS_VISIBLE) {
                if(Y_IS_VISIBLE)  outputArrays[x+2][y  ] = "<span class=mylime>)</span>";
            }
            break;

        case 1:
            if(X_IS_VISIBLE) {
                if(Y_IS_VISIBLE)   outputArrays[x  ][y  ] = "<span class=mylime>&lt;</span>";
                if(Y1_IS_VISIBLE)  outputArrays[x  ][y+1] = "<span class=mylime>&lt;</span>";
            }
            if(X1_IS_VISIBLE) {
                if(Y_IS_VISIBLE)   outputArrays[x+1][y  ] = "<span class=mylime>" + thisSpinner + "</span>";
                if(Y1_IS_VISIBLE)  outputArrays[x+1][y+1] = "<span class=mylime>" + thisSpinner + "</span>";
            }
            if(X2_IS_VISIBLE) {
                if(Y_IS_VISIBLE)   outputArrays[x+2][y  ] = "<span class=mylime>\\</span>";
                if(Y1_IS_VISIBLE)  outputArrays[x+2][y+1] = "<span class=mylime>/</span>";
            }
            break;

        case 2:
            if(X_IS_VISIBLE) {
                if(YM1_IS_VISIBLE)  outputArrays[x  ][y-1] = "<span class=mylime>&lt;</span>";
                if(Y1_IS_VISIBLE)   outputArrays[x  ][y+1] = "<span class=mylime>&lt;</span>";
            }
            if(X1_IS_VISIBLE) {
                if(YM1_IS_VISIBLE)  outputArrays[x+1][y-1] = "<span class=mylime>o</span>";
                if(Y_IS_VISIBLE)    outputArrays[x+1][y  ] = "<span class=mylime>&lt;</span>";
                if(Y1_IS_VISIBLE)   outputArrays[x+1][y+1] = "<span class=mylime>o</span>";
            }
            if(X2_IS_VISIBLE) {
                if(YM1_IS_VISIBLE)  outputArrays[x+2][y-1] = "<span class=mylime>" + thisSpinner + "</span>";
                if(Y_IS_VISIBLE)    outputArrays[x+2][y  ] = "<span class=mylime>o</span>";
                if(Y1_IS_VISIBLE)   outputArrays[x+2][y+1] = "<span class=mylime>" + thisSpinner + "</span>";
            }
            if(X3_IS_VISIBLE) {
                if(YM1_IS_VISIBLE)  outputArrays[x+3][y-1] = "<span class=mylime>\\</span>";
                if(Y_IS_VISIBLE)    outputArrays[x+3][y  ] = "<span class=mylime>" + thisSpinner + "</span>";
                if(Y1_IS_VISIBLE)   outputArrays[x+3][y+1] = "<span class=mylime>/</span>";
            }
            if(X4_IS_VISIBLE) {
                if(Y_IS_VISIBLE)    outputArrays[x+4][y  ] = "<span class=mylime>)</span>";
            }
            break;

        case 3:
            if(X_IS_VISIBLE) {
                if(YM1_IS_VISIBLE)  outputArrays[x  ][y-1] = "<span class=mylime>&lt;</span>";
                if(Y2_IS_VISIBLE)   outputArrays[x  ][y+2] = "<span class=mylime>&lt;</span>";
            }
            if(X1_IS_VISIBLE) {
                if(YM1_IS_VISIBLE)  outputArrays[x+1][y-1] = "<span class=mylime>o</span>";
                if(Y_IS_VISIBLE)    outputArrays[x+1][y  ] = "<span class=mylime>&lt;</span>";
                if(Y1_IS_VISIBLE)   outputArrays[x+1][y+1] = "<span class=mylime>&lt;</span>";
                if(Y2_IS_VISIBLE)   outputArrays[x+1][y+2] = "<span class=mylime>o</span>";
            }
            if(X2_IS_VISIBLE) {
                if(YM1_IS_VISIBLE)  outputArrays[x+2][y-1] = "<span class=mylime>" + thisSpinner + "</span>";
                if(Y_IS_VISIBLE)    outputArrays[x+2][y  ] = "<span class=mylime>o</span>";
                if(Y1_IS_VISIBLE)   outputArrays[x+2][y+1] = "<span class=mylime>o</span>";
                if(Y2_IS_VISIBLE)   outputArrays[x+2][y+2] = "<span class=mylime>" + thisSpinner + "</span>";
            }
            if(X3_IS_VISIBLE) {
                if(YM1_IS_VISIBLE)  outputArrays[x+3][y-1] = "<span class=mylime>\\</span>";
                if(Y_IS_VISIBLE)    outputArrays[x+3][y  ] = "<span class=mylime>" + thisSpinner + "</span>";
                if(Y1_IS_VISIBLE)   outputArrays[x+3][y+1] = "<span class=mylime>" + thisSpinner + "</span>";
                if(Y2_IS_VISIBLE)   outputArrays[x+3][y+2] = "<span class=mylime>/</span>";
            }
            if(X4_IS_VISIBLE) {
                if(Y_IS_VISIBLE)    outputArrays[x+4][y  ] = "<span class=mylime>\\</span>";
                if(Y1_IS_VISIBLE)   outputArrays[x+4][y+1] = "<span class=mylime>/</span>";
            }
            break;
    }
}

function drawProgressBar(thisPercentage) {
   var strOutput = "";
   if(thisPercentage == 100.0) {
      strOutput += "<span class=mygreen>[Charged!]</span>";
   } else {
      strOutput += "<span class=mygrey>";
      var barLength = parseInt(thisPercentage / 10.0);
      for(var i=0; i<barLength;        i++)  strOutput += "o";
      for(var i=0; i<(10 - barLength); i++)  strOutput += "-";
      strOutput += "</span>";
   }
   return strOutput;
}

function drawDamageRepairBar() {
   var strOutput = "<span class=mypink>";
   var thisPercentage = (PLAYER_RECOVERY_COUNTDOWN_DURATION - playerRecoveryCountdown) / PLAYER_RECOVERY_COUNTDOWN_DURATION;
   var barLength = parseInt(thisPercentage * 20.0);
   for(var i=0; i<barLength;        i++)  strOutput += "o";
   for(var i=0; i<(20 - barLength); i++)  strOutput += "-";
   strOutput += "</span>";
   return strOutput;
}

function alignLeft(thisVal) {
   if(thisVal < 10)     return "" + thisVal + "    ";
   if(thisVal < 100)    return "" + thisVal + "   ";
   if(thisVal < 1000)   return "" + thisVal + "  ";
   if(thisVal < 10000)  return "" + thisVal + " ";
   return "" + thisVal;
}


//--------------//
//  USER INPUT  //
//--------------//

function kp(e) {

    var keyCode = "";
    if(e) {
       if(e.keyCode)     keyCode = e.keyCode;  // Safari
       else if(e.which)  keyCode = e.which;    // Firefox
    } else {
        if(window.event) {
            if(window.event.keyCode)  keyCode = window.event.keyCode;  // IE
        }
    }
    if(keyCode == "")  return;

    if(keyCode == 103)    started = true;  // g - go
    if(started == false)  return;
   
    if(inShop == false) {
        if(keyCode == 112)  paused = !paused;  // p - pause
        if(paused == false) {
            if(keyCode == 113)  playerY--;                           // q - player up
            if(keyCode == 97)   playerY++;                           // a - player down
            if(keyCode == 32)   fireInvinciball();                   // space - fire invinciball
            if(keyCode == 99)   cash += 100;                         // c - add cash (cheat)
            if(keyCode == 118)  cheatInvincible = !cheatInvincible;  // v - invincibility (cheat)
            if(playerY <  0)       playerY = 0;
            if(playerY >= HEIGHT)  playerY = HEIGHT - 1;
        }
    }
    
    if(keyCode == 111) {  // o - enter or leave shop
       inShop = !inShop;
       if(inShop == true)  paused = true;
       else                autoconfigurePowerups();
    }
    
    if(inShop == true) {
       if(keyCode == 113)  shopSelection--;  // q - shop selection up
       if(keyCode == 97)   shopSelection++;  // a - shop selection down
       if(keyCode == 108) {                  // l - buy
          if(shopSelection == SHOP_LASERS   && cash >= PRICE_LASERS   && lasersPowerupLevel   < MAX_LEVEL_LASERS)    { cash -= PRICE_LASERS;    lasersPowerupLevel++;   }
          if(shopSelection == SHOP_SHIELD   && cash >= PRICE_SHIELD   && shieldPowerupLevel   < MAX_LEVEL_SHIELD)    { cash -= PRICE_SHIELD;    shieldPowerupLevel++;   }
          if(shopSelection == SHOP_ORBITERS && cash >= PRICE_ORBITERS && orbitersPowerupLevel < MAX_LEVEL_ORBITERS)  { cash -= PRICE_ORBITERS;  orbitersPowerupLevel++; }
          if(shopSelection == SHOP_MISSILES && cash >= PRICE_MISSILES && missilesPowerupLevel < MAX_LEVEL_MISSILES)  { cash -= PRICE_MISSILES;  missilesPowerupLevel++; }
          if(shopSelection == SHOP_GATLING  && cash >=PRICE_GATLING   && gatlingPowerupLevel  < MAX_LEVEL_GATLING)   { cash -= PRICE_GATLING;   gatlingPowerupLevel++;  }
       }
       if(keyCode == 107) {                  // k - sell
          if(shopSelection == SHOP_LASERS   && lasersPowerupLevel   > 0)  { lasersPowerupLevel--;    cash += PRICE_LASERS;   }
          if(shopSelection == SHOP_SHIELD   && shieldPowerupLevel   > 0)  { shieldPowerupLevel--;    cash += PRICE_SHIELD;   }
          if(shopSelection == SHOP_ORBITERS && orbitersPowerupLevel > 0)  { orbitersPowerupLevel--;  cash += PRICE_ORBITERS; }
          if(shopSelection == SHOP_MISSILES && missilesPowerupLevel > 0)  { missilesPowerupLevel--;  cash += PRICE_MISSILES; }
          if(shopSelection == SHOP_GATLING  && gatlingPowerupLevel  > 0)  { gatlingPowerupLevel--;   cash += PRICE_GATLING;  }
       }
       if(keyCode == 110) {                  // n - decrease min
          if(shopSelection == SHOP_LASERS   && lasersAutoconfigureMin > 0)  lasersAutoconfigureMin--;
          if(shopSelection == SHOP_SHIELD   && shieldAutoconfigureMin > 0)  shieldAutoconfigureMin--;
       }
       if(keyCode == 109) {                  // m - increase min
          if(shopSelection == SHOP_LASERS   && lasersAutoconfigureMin < MAX_LEVEL_LASERS)  lasersAutoconfigureMin++;
          if(shopSelection == SHOP_SHIELD   && shieldAutoconfigureMin < MAX_LEVEL_SHIELD)  shieldAutoconfigureMin++;
       }
       if(shopSelection < 0)  shopSelection = 0;
       if(shopSelection > 4)  shopSelection = 4;
    }
    
    if(paused == true || inShop == true) {
        updateDisplay();
    }
}

function fireInvinciball() {
   if(invinciballCharging != 100.0)  return;
   if(playerRecoveryCountdown > 0)   return;
   invinciballCharging = 0.0;
   spawnNewBullet(playerX, playerY, BULLET_TYPE_INVINCIBALL);
}

document.onkeypress = kp;