// Helpers: Cookie access.
function createCookie(name,value,days) { name = "swarm" + name; var expires = ""; if (days) { var date = new Date(); date.setDate(date.getDate() + days); expires = "; expires=" + date.toGMTString(); } document.cookie = name + "=" + value + expires + "; path=/"; }
function readCookie(name) { name = "swarm" + 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) { var v = c.substring(nameEQ.length, c.length); return v; } } return null; }
function eraseCookie(name) { createCookie(name, "" , -1); }

// Helpers: String builder.
function StringBuilder(value) { this.strings = [""]; if (value) this.append(value); }
StringBuilder.prototype.append = function(v) { this.strings.push(v); }
StringBuilder.prototype.appendList = function() { for (var i = 0; i < arguments.length; i++) this.strings.push(arguments[i]); }
StringBuilder.prototype.clear = function() { this.strings.length = 1; }
StringBuilder.prototype.toString = function() { return this.strings.join(""); }

String.prototype.reverse = function() { splitext = this.split(""); revertext = splitext.reverse(); reversed = revertext.join(""); return reversed; }

// Trace.js

function trace() { var sb = new StringBuilder(); for (var i = 0; i < arguments.length; ++i) sb.appendList(arguments[i], " "); alert(sb.toString()); }

// Physics.js

function v_(x, y) { return {x:x, y:y}; }
function v_c(a) { return {x:a.x, y:a.y}; }
function v_add(a, b) { return {x:a.x + b.x, y:a.y + b.y}; }
function v_sub(a, b) { return {x:a.x - b.x, y:a.y - b.y}; }
function v_mul(a, b) { return {x:a.x * b, y:a.y * b}; }
function v_madd(a, b, c) { return {x:a.x + b.x * c, y: a.y + b.y * c}; }
function v_dot(a, b) { return a.x * b.x + a.y * b.y; }
function v_len(a) { return Math.sqrt(v_dot(a, a)); }
function v_nor(a) { var inv_len = 1.0 / Math.sqrt(a.x * a.x + a.y * a.y); return {x:a.x * inv_len, y:a.y * inv_len}; }
function v_nor_2(a, b) { var inv_len = 1.0 / Math.sqrt(a * a + b * b); return {x:a * inv_len, y:b * inv_len}; }
function p_(x, y, z) { return {x:x, y:y, z:z}; }
function p_dot(p, v) { return p.x * v.x + p.y * v.y + p.z; }
function v_crs(a) { return {x:-a.y, y:a.x}; }
function v_dist(a, b) { return Math.sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y)); }
function v_dist_sq(a, b) { return (a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y); }
function v_dir(a, b, c) { return v_mul(v_nor_2(b.x - a.x, b.y - a.y), c); }
function v_madd_ref(a, b, c) { a.x += b.x * c; a.y += b.y * c; }
function v_add_ref(a, b) { a.x += b.x; a.y += b.y; }
function v_mul_ref(a, b) { a.x *= b; a.y *= b; }
function v_sub_ref(a, b) { a.x -= b.x; a.y -= b.y; }
function v_plane_dist(a, x, y) { return a.a * x + a.b * y + a.c; }
function v_plane_distance(a, b, c) {
  var d = Math.sqrt((b.x - c.x) * (b.x - c.x) + (b.y - c.y) * (b.y - c.y));

  return ((b.y - c.y) * a.x + (c.x - b.x) * a.y + (b.x * c.y - b.y * c.x)) / d;
}

var ph = ph || {};

ph.particles = [];
ph.free_list = [];
ph.attractors = [];
ph.asteroids = [];
ph.ship = null;
ph.active = 0;
ph.capacity = 0;
ph.gravity = 2;
ph.fps = 25;
ph.time_per_frame_milliseconds = 1000 / ph.fps;
ph.time_per_frame = 1.0 / ph.fps;
ph.initial_speed = 50;
ph.multiplier = 1;
ph.color = 0;
ph.color_step = 0;
ph.tick = 0;
ph.score = 0;
ph.current_score = 0;
ph.powerup_ready = 0;
ph.powerup_timeout = 0;
ph.powerup_strength = 0;
ph.powerup_type = 0;
ph.powerup_c = v_(0, 0);
ph.powerup_v = v_(0, 0);
ph.weapon_ready = 0;
ph.weapon_timeout = 0;
ph.weapon_strength = 0;
ph.weapon_type = 0;
ph.circles = [];
ph.game_end_called = false;

ph.particle = function(x, y, m, dx, dy, free) {
  return {c: v_(x, y), l: v_(x, y), v: v_(dx, dy), m: m, free: free, first: true, dead: false, inside: false, blast: false};
};

ph.reset = function(capacity) {
  var _ph = ph;

  _ph.max_health = 50;
  _ph.health = 50;
  _ph.health_cooldown = 0;
  _ph.score = 0;
  _ph.current_score = 0;

  _ph.powerup_ready = 600;

  _ph.particles = [];
  _ph.circles = [];

  _ph.game_end_called = false;

  _ph.colors = [
    {r: 40, g:60, b:255},
    {r: 40, g: 255, b: 255},
    {r: 150, g: 60, b: 255},    
    {r: 40, g: 60, b:255},    
    {r: 240, g: 240, b: 240},
    {r: 40, g: 60, b:255}    
  ];

  var base_color = "rgba(40, 60, 255, ";
  _ph.color_a = base_color + "1)";
  _ph.color_b = base_color + "0.7)";
  _ph.color_c = base_color + "0.5)";
  _ph.color_d = base_color + "0.2)";

  _ph.color_step = (_ph.colors.length - 1) / 200;
  _ph.color = 0;

  for (var i = 0; i < capacity; ++i) {
    var p = _ph.particle(0, 0, 1, 0, 0, true);

    _ph.particles.push(p);
  }

  _ph.free_list = []; 
  for (var i = 0; i < capacity; ++i)
    _ph.free_list.push(_ph.particles[i]);
  _ph.active = 0;
  _ph.capacity = capacity;
  _ph.remainder = 0;

  _ph.attractors = [];

  _ph.asteroids = [];
};

ph.add_attractor = function(x, y, m, tag) {
  var _ph = ph;

  var a = {x: x, y: y, m: m, tag: tag, type: null};

  _ph.attractors.push(a);

  return a;
}

ph.particles_to_spawn = function(game_time) {
  var _ph = ph;

  _ph.remainder += (_game_mode == 0 ? 50 : 35) * game_time;

	var particles = Math.floor(_ph.remainder);
	_ph.remainder -= particles;

	return particles;
};

ph.create_asteroid = function(c, v, rv, m, radius, type, massless) {
  var a = {c: v_c(c), v: v_c(v), cr: v_c(c), rv: rv, m: m, r: 0, move_radius: 0, total_radius: radius, r2: 0, rv2: 0, radius: radius, d: [], type: type, start_type: type, massless: massless};

  for (var i = 0; i < 5; ++i) {
    var angle = (72 * i + (Math.random() * 30 - 15)) * Math.PI / 180;

    var r = radius + Math.random() * radius / 3 - radius / 6;

    a.d.push(v_(r * Math.cos(angle), r * Math.sin(angle)));
  }

  ph.asteroids.push(a);
};

ph.create_asteroid_2 = function(c, v, rv, move_radius, r2, rv2, m, radius, type) {
  var a = {c: v_c(c), v: v_c(v), cr: v_c(c), rv: rv, m: m, r: 0, move_radius: move_radius, total_radius: radius + move_radius, r2: r2, rv2: rv2, radius: radius, d: [], type: type, start_type: type};
                                  
  for (var i = 0; i < 5; ++i) {
    var angle = (72 * i + (Math.random() * 30 - 15)) * Math.PI / 180;

    var r = radius + Math.random() * radius / 3 - radius / 6;

    a.d.push(v_(r * Math.cos(angle), r * Math.sin(angle)));
  }

  ph.asteroids.push(a);
};

ph.set_ship = function(c, m, color, radius) {
  var s = {c: v_c(c), l: v_c(c), v: v_(0, 0), m: m, r: - Math.PI / 2, r_target: - Math.PI / 2, radius: radius, d: [ v_(0, 10), v_(-10, -10), v_(10, -10) ], color: color};
                                                                                                             
  ph.ship = s;
};

ph.add_circle = function(x, y, r, color) {
  ph.circles.push({x: x, y: y, r: 0, target: r, color: color});
}

ph.step = function(game_time) {
  var _ph = ph;

  ++_ph.tick;

  var ci = Math.floor(_ph.color);
  var cj = ci + 1;
  var t = _ph.color - ci;

  var r = _ph.colors[ci].r * (1 - t) + _ph.colors[cj].r * t;
  var g = _ph.colors[ci].g * (1 - t) + _ph.colors[cj].g * t;
  var b = _ph.colors[ci].b * (1 - t) + _ph.colors[cj].b * t;

  // Particle color change.
  var base_color = "rgba(" + Math.floor(r) + ", " + Math.floor(g) + ", " + Math.floor(b);
  _ph.color_a = base_color + ", 1)";
  _ph.color_b = base_color + ", 0.6)";
  _ph.color_c = base_color + ", 0.2)";

  _ph.color += _ph.color_step;
  if (_ph.color >= _ph.colors.length - 1)
    _ph.color -= _ph.colors.length - 1;

  var s = _ph.ship;

  // Process wave circles.
  for (var i = 0; i < _ph.circles.length;) {
    var c = _ph.circles[i];

    c.r += 3;
    if (c.r > c.target) {
      _ph.circles[i] = _ph.circles[0];
      _ph.circles.shift();
    } else {
      ++i;
    }
  }

  // Process asteroids movement.
  for (var i = 0; i < _ph.asteroids.length; ++i) {
    var a = _ph.asteroids[i];

    v_madd_ref(a.c, a.v, game_time);
    a.r += a.rv * game_time;

    if (a.type == 5 || a.type == 5.33 || a.type == 5.66) {
      var dx = s.c.x - a.c.x;
      var dy = s.c.y - a.c.y;
      var l_squared = dx * dx + dy * dy;
      if (l_squared > 1) {
        var inv_length = 1.0 / Math.sqrt(l_squared);

        var f = 40;
        if (_free_flight == 0)
          f = 15;
        a.v.x = dx * inv_length * f;
        a.v.y = dy * inv_length * f;
      }      
    } else if (_free_flight == 0) { 
      var dx = s.c.x - a.c.x;
      var dy = s.c.y - a.c.y;
      var l_squared = dx * dx + dy * dy;
      if (l_squared > 4900) {
        var inv_length = 1.0 / Math.sqrt(l_squared);

        var f;
        if (a.total_radius > 16) {
          a.v.x *= 1;
          a.v.y *= 1;
          
          f = 1.7;
        } else {
          a.v.x *= 0.97;
          a.v.y *= 0.97;

          f = 1.0;
        }

        a.v.x += dx * inv_length * (a.massless ? 0.25 : f);
        a.v.y += dy * inv_length * (a.messless ? 0.25 : f);
      } else if (l_squared > 150) {
        var inv_length = 1.0 / Math.sqrt(l_squared);        

        var f = 1.5;
        if (a.total_radius > 16)
          f = 0.3;
        else {
          a.v.x *= 0.97;
          a.v.y *= 0.97;
        }        

        a.v.x += dx * inv_length * (a.massless ? 0.15 : f);
        a.v.y += dy * inv_length * (a.massless ? 0.15 : f);
      }

      var v = a.v.x * a.v.x + a.v.y * a.v.y;
      if (v < 1600) {
        v = 40.0 / Math.sqrt(v);
        a.v.x *= v;
        a.v.y *= v;
      }
    }

    if (a.c.x > 320 + a.total_radius)
      a.c.x -= 320 + 2 * a.total_radius;
    if (a.c.x < - a.total_radius)
      a.c.x += 320 + 2 * a.total_radius;
    if (a.c.y > 320 + a.total_radius)
      a.c.y -= 320 + 2 * a.total_radius;
    if (a.c.y < - a.total_radius)
      a.c.y += 320 + 2 * a.total_radius;

    a.rcos = Math.cos(-a.r);
    a.rsin = Math.sin(-a.r);

    a.r2 += a.rv2 * game_time;
    a.cr.x = a.c.x + a.move_radius * Math.cos(-a.r2);
    a.cr.y = a.c.y + a.move_radius * Math.sin(-a.r2);
  }

  if (_free_flight) {  
    // Process attractors movement.  
    for (var j = _ph.attractors.length - 1; j >= 0; --j) {
      var a = _ph.attractors[j];
      var dx = a.x - s.c.x;
      var dy = a.y - s.c.y;
      var l_squared = dx * dx + dy * dy;
      if (l_squared < 1)
        continue;

      var inv_length = 1.0 / Math.sqrt(l_squared);

      if (l_squared < 10000)
        l_squared = 10000;
        
      var force = game_time * a.m * s.m * 800 / l_squared;

      s.v.x += dx * inv_length * force;
      s.v.y += dy * inv_length * force;
    }

    // Process ship movement.
    s.c.x += s.v.x * game_time;
    s.c.y += s.v.y * game_time;

    if (s.c.x != s.l.x || s.c.y != s.l.y) {
      s.r = Math.atan2(s.c.y - s.l.y, s.c.x - s.l.x) - Math.PI / 2;
    }
  } else {
    s.r = s.r_target * 0.4 + s.r * 0.6;
  }

  var v = s.v.x * s.v.x + s.v.y * s.v.y;
  if (v > 10000) {
    v = 100.0 / Math.sqrt(v);
    s.v.x *= v;
    s.v.y *= v;
  }

  if (s.c.x > 320)
    s.c.x -= 320;
  if (s.c.x < 0)
    s.c.x += 320;
  if (s.c.y > 320)
    s.c.y -= 320;
  if (s.c.y < 0)
    s.c.y += 320;
                   
  s.l.x = s.c.x;
  s.l.y = s.c.y;

  // Check health loss.
  var died = false;

  if (_ph.health > 0) {
    var bumping = false;

    for (var i = 0; i < _ph.asteroids.length; ++i) {
      var a = _ph.asteroids[i];

      var sr = s.radius;
      if (_free_flight == 0)
        sr *= 1.3;

      if (v_dist(a.cr, s.c) < a.radius + sr) {
        if (_ph.health_cooldown == 0) {
          if (_ph.powerup_type != 3 || _ph.powerup_strength == 0) {
            _ph.health -= 1;
            _ph.score -= 5;
            if (_ph.score < 0)
              _ph.score = 0;
          }
        }
        else if (++_ph.health_cooldown == 3) 
  				_ph.health_cooldown = 0;
      }
    }
    
    if (_ph.health <= 0)
      died = true;
  }

  if (!bumping) 
    _ph.health_cooldown = 0;

  // Player died, do a final salute.
  if (died) {
    for (var i = 0; i < 360; i += 10) {
      if (_ph.active < _ph.capacity) {
        var a = i * Math.PI / 180;

        var p = _ph.free_list.shift();

        p.free = false;
        p.m = 10;

        p.v = v_(Math.cos(a), Math.sin(a));
        p.c = v_(s.c.x, s.c.y) ;
        p.v = v_mul(p.v, ph.initial_speed);

        p.l = v_c(p.c);

        ++_ph.active;
      }
    }
  }

  // Spawn bullets.
  if (_ph.health > 0 && _ph.tick % 5 == 0 && _ph.active < _ph.capacity) {
    if (_free_flight == 0) {
      _ph.score -= 5;
      if (_ph.score < 0)
        _ph.score = 0;
    }

    var p = _ph.free_list.shift();
    if (p) {
      p.free = false; p.m = 10;

      p.v = v_(Math.cos(s.r + Math.PI / 2), Math.sin(s.r + Math.PI / 2));
      p.c = v_(s.c.x + 12 * p.v.x, s.c.y + 12 * p.v.y) ;
      p.v = v_mul(p.v, ph.initial_speed * 4); // do not combine with above.
      p.l = v_c(p.c);

      ++_ph.active;
    }

    if (_ph.powerup_type == 4 && _ph.powerup_strength > 0) {
      p = _ph.free_list.shift();
      if (p) {
        p.free = false; p.m = 10;

        p.v = v_(Math.cos(s.r + Math.PI / 2 - Math.PI / 8), Math.sin(s.r + Math.PI / 2 - Math.PI / 8));
        p.c = v_(s.c.x + 12 * p.v.x, s.c.y + 12 * p.v.y) ;
        p.v = v_mul(p.v, ph.initial_speed * 4); // do not combine with above.
        p.l = v_c(p.c);

        ++_ph.active;
      }

      p = _ph.free_list.shift();
      if (p) {
        p.free = false; p.m = 10;

        p.v = v_(Math.cos(s.r + Math.PI / 2 + Math.PI / 8), Math.sin(s.r + Math.PI / 2 + Math.PI / 8));
        p.c = v_(s.c.x + 12 * p.v.x, s.c.y + 12 * p.v.y) ;
        p.v = v_mul(p.v, ph.initial_speed * 4); // do not combine with above.
        p.l = v_c(p.c);

        ++_ph.active;
      }
    }
  }

  // Process particles.
  for (var i = 0, c = _ph.capacity; i < c; ++i) {
    var p = _ph.particles[i];
    if (p.free || p.dead)
      continue;
        
    p.l = v_c(p.c);

    p.c.x += p.v.x * game_time;
    p.c.y += p.v.y * game_time;
    
    if (p.c.x < 0 || p.c.y < 0 || p.c.x > 320 || p.c.y > 320) {
      --_ph.active;
      p.free = true;
      _ph.free_list.push(p);
    }

    if (p.blast) 
      continue;

    for (var j = 0; j < _ph.asteroids.length; ++j) {
      var a = _ph.asteroids[j];

      if (v_dist_sq(p.c, a.cr) < a.radius * a.radius) {
        // Bullet-asteroid collision.
        // Kill the particle.
        p.free = true;
        _ph.free_list.push(p);
        --_ph.active;

        // Slow down asteroid.
        if (_free_flight == 0) {
          var d = v_nor(v_(a.c.x - 160, a.c.y - 160));

          a.v.x += d.x * 20;
          a.v.y += d.y * 20;
        }

        // Score using the original asteroid type.
        if (a.start_type == 0)
          _ph.score += 100;
        else if (a.start_type == 1)
          _ph.score += 200;
        else if (a.start_type == 2)
          _ph.score += 300;         
        else if (a.start_type == 3)
          _ph.score += 100;
        else if (a.start_type == 4)
          _ph.score += 100;
        else if (a.start_type == 5)
          _ph.score += 400;
        else if (a.start_type == 6)
          _ph.score += 600;

        // Continue depending on asteroid type.
        if (a.type == 0) {
          draw_asteroid($("#game-canvas").get(0).getContext("2d"), a, true);

          _ph.asteroids[j] = _ph.asteroids[0];
          _ph.asteroids.shift();
        } else if (a.type == 1) {
          draw_asteroid($("#game-canvas").get(0).getContext("2d"), a, true);
          
          _ph.asteroids[j] = _ph.asteroids[0];
          _ph.asteroids.shift();

          var angle = Math.random() * 2 * Math.PI;

          ph.create_asteroid(a.c, v_mul(v_(Math.cos(angle + Math.PI * 0 / 3), Math.sin(angle + Math.PI * 0 / 3)), 20), Math.random() * 2 - 1, 10, 10, 0);
          ph.create_asteroid(a.c, v_mul(v_(Math.cos(angle + Math.PI * 2 / 3), Math.sin(angle + Math.PI * 2 / 3)), 20), Math.random() * 2 - 1, 10, 10, 0);
          ph.create_asteroid(a.c, v_mul(v_(Math.cos(angle + Math.PI * 4 / 3), Math.sin(angle + Math.PI * 4 / 3)), 20), Math.random() * 2 - 1, 10, 10, 0);
        } else if (a.type == 1.25) { 
          a.type = 1;
        } else if (a.type == 1.5) { 
          a.type = 1.25;
        } else if (a.type == 1.75) {
          a.type = 1.5;
        } else if (a.type == 2) {
          a.type = 1.75;
        } else if (a.type == 3) {
          _ph.asteroids[j] = _ph.asteroids[0];
          _ph.asteroids.shift();

          var rnd_properties = game_random_position(15);
          ph.create_asteroid(rnd_properties.c, rnd_properties.v, rnd_properties.r, 10, 10, 0, true);
          var rnd_properties = game_random_position(15);
          ph.create_asteroid(rnd_properties.c, rnd_properties.v, rnd_properties.r, 10, 10, 0, true);
          var rnd_properties = game_random_position(15);
          ph.create_asteroid(rnd_properties.c, rnd_properties.v, rnd_properties.r, 10, 10, 0, true);
          var rnd_properties = game_random_position(15);
          ph.create_asteroid(rnd_properties.c, rnd_properties.v, rnd_properties.r, 10, 10, 0, true);
          var rnd_properties = game_random_position(15);
          ph.create_asteroid(rnd_properties.c, rnd_properties.v, rnd_properties.r, 10, 10, 0, true);
          var rnd_properties = game_random_position(15);
          ph.create_asteroid(rnd_properties.c, rnd_properties.v, rnd_properties.r, 10, 10, 0, true);
          var rnd_properties = game_random_position(15);
          ph.create_asteroid(rnd_properties.c, rnd_properties.v, rnd_properties.r, 10, 10, 0, true);
          var rnd_properties = game_random_position(15);
          ph.create_asteroid(rnd_properties.c, rnd_properties.v, rnd_properties.r, 10, 10, 0, true);
          var rnd_properties = game_random_position(15);
          ph.create_asteroid(rnd_properties.c, rnd_properties.v, rnd_properties.r, 10, 10, 0, true);
          var rnd_properties = game_random_position(15);
          ph.create_asteroid(rnd_properties.c, rnd_properties.v, rnd_properties.r, 10, 10, 0, true);
          var rnd_properties = game_random_position(15);
          ph.create_asteroid(rnd_properties.c, rnd_properties.v, rnd_properties.r, 10, 10, 0, true);
          var rnd_properties = game_random_position(15);
          ph.create_asteroid(rnd_properties.c, rnd_properties.v, rnd_properties.r, 10, 10, 0, true);
          var rnd_properties = game_random_position(15);
          ph.create_asteroid(rnd_properties.c, rnd_properties.v, rnd_properties.r, 10, 10, 0, true);
          var rnd_properties = game_random_position(15);
          ph.create_asteroid(rnd_properties.c, rnd_properties.v, rnd_properties.r, 10, 10, 0, true);
          var rnd_properties = game_random_position(15);
          ph.create_asteroid(rnd_properties.c, rnd_properties.v, rnd_properties.r, 10, 10, 0, true);
          var rnd_properties = game_random_position(15);
          ph.create_asteroid(rnd_properties.c, rnd_properties.v, rnd_properties.r, 10, 10, 0, true);
          var rnd_properties = game_random_position(15);
          ph.create_asteroid(rnd_properties.c, rnd_properties.v, rnd_properties.r, 10, 10, 0, true);
          var rnd_properties = game_random_position(15);
          ph.create_asteroid(rnd_properties.c, rnd_properties.v, rnd_properties.r, 10, 10, 0, true);
        }  else if (a.type == 4) {
          _ph.asteroids[j] = _ph.asteroids[0];
          _ph.asteroids.shift();

          var rnd_properties = game_random_position(35);
          ph.create_asteroid(rnd_properties.c, rnd_properties.v, rnd_properties.r, 10, 10, 0, true);
          var rnd_properties = game_random_position(35);
          ph.create_asteroid(rnd_properties.c, rnd_properties.v, rnd_properties.r, 10, 10, 0, true);
          var rnd_properties = game_random_position(35);
          ph.create_asteroid(rnd_properties.c, rnd_properties.v, rnd_properties.r, 10, 10, 0, true);

          var rnd_properties = game_random_position(35);
          ph.create_asteroid(rnd_properties.c, rnd_properties.v, rnd_properties.r, 10, 10, 0, true);
          var rnd_properties = game_random_position(35);
          ph.create_asteroid(rnd_properties.c, rnd_properties.v, rnd_properties.r, 10, 10, 0, true);
          var rnd_properties = game_random_position(35);
          ph.create_asteroid(rnd_properties.c, rnd_properties.v, rnd_properties.r, 10, 10, 0, true);

          var rnd_properties = game_random_position(35);
          ph.create_asteroid(rnd_properties.c, rnd_properties.v, rnd_properties.r, 10, 10, 0, true);
          var rnd_properties = game_random_position(35);
          ph.create_asteroid(rnd_properties.c, rnd_properties.v, rnd_properties.r, 10, 10, 0, true);
          var rnd_properties = game_random_position(35);
          ph.create_asteroid(rnd_properties.c, rnd_properties.v, rnd_properties.r, 10, 10, 0, true);

          var rnd_properties = game_random_position(35);
          ph.create_asteroid(rnd_properties.c, rnd_properties.v, rnd_properties.r, 10, 10, 0, true);
          var rnd_properties = game_random_position(35);
          ph.create_asteroid(rnd_properties.c, rnd_properties.v, rnd_properties.r, 10, 10, 0, true);
          var rnd_properties = game_random_position(35);
          ph.create_asteroid(rnd_properties.c, rnd_properties.v, rnd_properties.r, 10, 10, 0, true);
        } else if (a.type == 5) {
          _ph.asteroids[j] = _ph.asteroids[0];
          _ph.asteroids.shift();
        } else if (a.type == 6) {
          a.type = 5.66;
        } else if (a.type == 5.66) {
          a.type = 5.33;
        } else if (a.type == 5.33) {
          a.type = 5;
        }

        break;
      }
    }
  }

  // Check if game has ended.
  if (_ph.health <= 0 && _ph.active <= 0) {
    if (!_ph.game_end_called)
      game_end();
    _ph.game_end_called = true;
  }

  // Check attractors expiration.
  for (var i = 0; i < _ph.attractors.length;) {
    var a = _ph.attractors[i];

    a.time_to_live -= game_time;
    if (a.time_to_live < a.lower_boundary) {
      a.m -= game_time * a.decrease;
      if (a.m < 0)
        a.m = 0;
    }

    if (a.time_to_live < 0) {
      _ph.attractors[i] = _ph.attractors[0];

      _ph.attractors.shift();

    } else {
      ++i;
    }
  }

  // Check if powerup is ready.
  if (_ph.powerup_type == 0 && _ph.powerup_ready > 0 && --_ph.powerup_ready == 0) {
    _ph.powerup_type = Math.floor(Math.random() * 4) + 1;
    if (_free_flight == 0) {
      _ph.powerup_type = Math.floor(Math.random() * 5) + 1;
      if (_ph.powerup_type == 5)
        _ph.powerup_type = 1;
    }

    _ph.powerup_timeout = _free_flight ? 400 : 600;
    
    var rnd_properties = game_random_position(15);

    _ph.powerup_c = rnd_properties.c;
    _ph.powerup_v = rnd_properties.v;
  }
  
  // Check if power up has expired.
  if (_ph.powerup_type != 0 && _ph.powerup_timeout > 0 && --_ph.powerup_timeout == 0) {
    _ph.powerup_ready = 200;
    _ph.powerup_type = 0;
  }

  if (_ph.powerup_type != 0 && _ph.powerup_strength > 0 && --_ph.powerup_strength == 0) {
    _ph.powerup_type = 0;
    _ph.powerup_ready = 350;
    _ph.powerup_timeout = 0;
  }

  // Move powerup.
  if (_ph.powerup_type != 0 && _ph.powerup_ready == 0 && _ph.powerup_timeout > 0) {
    if (_free_flight == 0) {
      var dx = s.c.x - _ph.powerup_c.x;
      var dy = s.c.y - _ph.powerup_c.y;

      var l_squared = dx * dx + dy * dy;
      if (l_squared > 4900) {
        var inv_length = 1.0 / Math.sqrt(l_squared);

        _ph.powerup_v.x += dx * inv_length * 0.3;
        _ph.powerup_v.y += dy * inv_length * 0.3;
      } else if (l_squared > 200) {
        var inv_length = 1.0 / Math.sqrt(l_squared);

        _ph.powerup_v.x += dx * inv_length * 0.7;
        _ph.powerup_v.y += dy * inv_length * 0.7;
      }
    }

    v_madd_ref(_ph.powerup_c, _ph.powerup_v, game_time);

    if (_free_flight == 0) {
      _ph.powerup_v.x *= 0.98;
      _ph.powerup_v.y *= 0.98;
    }

    if (_ph.powerup_c.x > 320)
      _ph.powerup_c.x -= 320;
    if (_ph.powerup_c.x < 0)
      _ph.powerup_c.x += 320;
    if (_ph.powerup_c.y > 320)
      _ph.powerup_c.y -= 320;
    if (_ph.powerup_c.y < 0)
      _ph.powerup_c.y += 320;

    // Check if player picked up powerup.
    if (v_dist(_ph.powerup_c, s.c) < s.radius + 14 && _ph.health > 0) {
      if (_ph.powerup_type == 1) {
        _ph.powerup_type = 0;
        _ph.powerup_ready = _free_flight == 0 ? 150 : 350;
        _ph.powerup_timeout = 0;

        _ph.health += 15;
        if (_ph.health > _ph.max_health)
          _ph.health = _ph.max_health;
      } else if (_ph.powerup_type == 2) {
        _ph.powerup_type = 0;
        _ph.powerup_ready = _free_flight == 0 ? 150 : 350;
        _ph.powerup_timeout = 0;

        for (var i = 0; i < 360; i += 15) {
          if (_ph.active < _ph.capacity) {
            var a = i * Math.PI / 180;

            var p = _ph.free_list.shift();

            p.free = false;
            p.m = 10;

            p.v = v_(Math.cos(a), Math.sin(a));
            p.c = v_(s.c.x, s.c.y) ;
            p.v = v_mul(p.v, ph.initial_speed * 4);

            p.l = v_c(p.c);

            ++_ph.active;
          }
        }
      } else if (_ph.powerup_type == 3) {
        _ph.powerup_ready = 0;
        _ph.powerup_timeout = 0;
        _ph.powerup_strength = _free_flight == 0 ? 200 : 300;
      } else if (_ph.powerup_type == 4) {
        _ph.powerup_ready = 0;
        _ph.powerup_timeout = 0;
        _ph.powerup_strength = _free_flight == 0 ? 200 : 300;
      }
    }
  }
};

ph.step_playground = function(game_time) {
  var _ph = ph;

  var ci = Math.floor(_ph.color);
  var cj = ci + 1;
  var t = _ph.color - ci;

  var r = _ph.colors[ci].r * (1 - t) + _ph.colors[cj].r * t;
  var g = _ph.colors[ci].g * (1 - t) + _ph.colors[cj].g * t;
  var b = _ph.colors[ci].b * (1 - t) + _ph.colors[cj].b * t;

  var base_color = "rgba(" + Math.floor(r) + ", " + Math.floor(g) + ", " + Math.floor(b);
  _ph.color_a = base_color + ", 1)";
  _ph.color_b = base_color + ", 0.6)";
  _ph.color_c = base_color + ", 0.2)";

  _ph.color += _ph.color_step;
  if (_ph.color >= _ph.colors.length - 1)
    _ph.color -= _ph.colors.length - 1;

  // Process wave circles.
  for (var i = 0; i < _ph.circles.length;) {
    var c = _ph.circles[i];

    c.r += 3;
    if (c.r > c.target) {
      _ph.circles[i] = _ph.circles[0];
      _ph.circles.shift();
    } else {
      ++i;
    }
  }

  for (var i = 0, c = _ph.capacity; i < c; ++i) {
    var p = _ph.particles[i];
    if (p.free || p.dead)
      continue;
        
    p.v.y += _ph.gravity * p.m * game_time;
    
    p.l = v_c(p.c);

    p.c.x += p.v.x * game_time;
    p.c.y += p.v.y * game_time;
    
    for (var j = _ph.attractors.length - 1; j >= 0; --j) {
      var a = _ph.attractors[j];
      var dx = a.x - p.c.x;
      var dy = a.y - p.c.y;
      var l_squared = dx * dx + dy * dy;
      if (l_squared < 1)
        continue;

      var inv_length = 1.0 / Math.sqrt(l_squared);

      if (l_squared < 10000)
        l_squared = 10000;
      
      var force = game_time * a.m * p.m * 800 / l_squared;

      p.v.x += dx * inv_length * force;
      p.v.y += dy * inv_length * force;
    }
    
    var v = p.v.x * p.v.x + p.v.y * p.v.y;
    if (v > 19600) {
      v = 140.0 / Math.sqrt(v);
      p.v.x *= v;
      p.v.y *= v;
    }

    if (p.c.x < 0 || p.c.y < 0 || p.c.x > 320 || p.c.y > 320) {
      --_ph.active;

      p.free = true;

      _ph.free_list.push(p);
    }
  }

  var particle = _ph.particles_to_spawn(game_time);
  while (_ph.active < _ph.capacity && particle > 0) {
    var p = _ph.free_list.shift();

    p.free = false;

    var dx = Math.random() * 3 - 1.5 + 160;
    var dy = Math.random() * 3 - 1.5;

    p.m = 10;
    p.c = v_(dx, dy);

    var x = Math.random() * 30 - 15;
    var y = Math.random() * 50 + 400;
 
    p.v = v_mul(v_nor(v_(x, y)), ph.initial_speed * 1);

    p.l = v_c(p.c);

    ++_ph.active;

    --particle;
  }

  for (var i = 0; i < _ph.attractors.length;) {
    var a = _ph.attractors[i];

    a.time_to_live -= game_time;
    if (a.time_to_live < a.lower_boundary) {
      a.m -= game_time * a.decrease;
      if (a.m < 0)
        a.m = 0;
    }

    if (a.time_to_live < 0) {
      _ph.attractors[i] = _ph.attractors[0];

      _ph.attractors.shift();

    } else {
      ++i;
    }
  }
};

function begin_touch(x, y) {
  var _ph = ph;

  while (_ph.attractors.length > 0)
    _ph.attractors.shift();

  var a = _ph.add_attractor(x, y, 400);
  _ph.add_circle(x, y, 40, "rgba(100, 150, 255,");

  a.time_to_live = 10000;
  a.lower_bound = 1;
  a.decrease = 0;
}

function move_touch(x, y) {
  var _ph = ph;

  if (_ph.attractors.length > 0) {
    _ph.attractors[0].x = x;
    _ph.attractors[0].y = y;
  }
}

function end_touch() {
  var _ph = ph;

  while (_ph.attractors.length > 0)
    _ph.attractors.shift();
}

// Touch stuff.
function touchHandler(event)
{
  if (_touch_mode == 0)
    return;

  var touches = event.changedTouches, first = touches[0], type = "";
  
  if (first.target.id != "game-canvas-clicker")
    return;
  
  switch(event.type)
  {
    case "touchstart":
      begin_touch(first.clientX, first.clientY);
      break;
    case "touchmove":
      move_touch(first.clientX, first.clientY);
      break;
    case "touchend":
      end_touch();
      break;
    default: return;
  }
  
  event.preventDefault();
}

function initTouchHandlers()
{
  document.addEventListener("touchstart", touchHandler, true);
  document.addEventListener("touchmove", touchHandler, true);
  document.addEventListener("touchend", touchHandler, true);    
} 


// Game control.
var _ajax_seed = 0;
var _stage = 0;
var _wave_counter = 0;
var _game_mode = 0;
var _free_flight = 0;
var _touch_mode = 0;

function game_start() {
  window.scrollTo(0, 1);

  $("#logo").animate({opacity: 0}, 400, function() {
    $("#logo").css("display", "none"); 

    game_begin();
  });  
}

function game_begin() {
  var canvas = $("#game-canvas").get(0).getContext("2d");

  canvas.fillStyle = "rgba(0, 0, 0, 1)";
  canvas.fillRect (0, 0, 320, 320);

  game_initialize();  

  $("#game-container").css("opacity", "0").css("display", "block");
  $("#game-container").animate({opacity: 1}, 400, function() {
    game_begin_continue();
  });
}

var _help_stage = 0;
var _help_start_game = false;

function game_begin_continue() {
  if (_game_mode == 0) {  
    if (readCookie("rulesShown") != "1") {
      
      _help_stage = 0;
      _help_start_game = true;

      $("#help-outer").css("background", "transparent url(imgs/help1.png) no-repeat 0px 0px");

      $("#help-container").css("opacity", "0").css("display", "block").animate(
        {opacity: 1}, 400);

      createCookie("rulesShown", "1", 365);
    } else
      start_simulator();
  } else {
    start_simulator();
  }
}

function game_initialize() {
  ++_ajax_seed;

  _stage = 0;
  _wave_counter = 0;

  if (_game_mode == 0) {
    ph.reset(150); 

    ph.set_ship(v_(160, 160), 10, "rgb(20, 200, 240)", 10);  

    $("#game-score").get(0).innerHTML = "";

    var rnd_properties = game_random_position(20);

    ph.create_asteroid(rnd_properties.c, rnd_properties.v, rnd_properties.r, 10, 20, 1);    
  } else {
    ph.reset(130); 

    $("#game-score").get(0).innerHTML = "";
  }
}

var _simulator_running = null;

function start_simulator() {
  window.scrollTo(0, 1);

  _simulator_running = setTimeout(game_simulate, ph.time_per_frame_milliseconds * ph.multiplier);
}

function stop_simulator() {
  if (_simulator_running) {
    clearTimeout(_simulator_running);
  
    _simulator_running = null;
  }
}

function game_random_position(v_max, offset) {
  var r = Math.random();

  var c;
  var v;

  if (!offset)
    offset = 0;  

  var f = 1; 
  var fr = Math.random() * 2 - 1;
  if (_game_mode == 0 && _free_flight == 0) {
    f = 0.2;
    fr = Math.random() * 0.5 + 0.5;
    if (Math.random() > 0.5)
      fr = -fr;
  }

  if (r < 0.25) {
    c = v_(-offset, Math.random() * 320);
    v = v_(f, fr);    
  } else if (r < 0.5) {
    c = v_(Math.random() * 320, 320 + offset);
    v = v_(fr, - f);    
  } else if (r < 0.75) {
    c = v_(320 + offset, Math.random() * 320);
    v = v_(- f, fr);    
  } else {
    c = v_(Math.random() * 320, -offset);
    v = v_(fr, f);
  }

  return {c: c, v: v_mul(v_nor(v), v_max), r: Math.random() * 2 - 1};
}

function draw_asteroid(canvas, a, override) {
  var rcos = Math.cos(a.r);
  var rsin = Math.sin(a.r);

  var x = a.cr.x + a.d[4].x * rcos - a.d[4].y * rsin;
  var y = a.cr.y + a.d[4].x * rsin + a.d[4].y * rcos;

  if (override) {
    canvas.strokeStyle = "rgb(255, 255, 255)";
  } else {
    if (a.type == 6) 
      canvas.strokeStyle = "rgb(240, 100, 240)";
    else if (a.type == 5.66) 
      canvas.strokeStyle = "rgb(240, 60, 240)";
    else if (a.type == 5.33) 
      canvas.strokeStyle = "rgb(240, 30, 240)";
    else if (a.type == 5) 
      canvas.strokeStyle = "rgb(240, 0, 240)";
    else if (a.type == 2) 
      canvas.strokeStyle = "rgb(240, 0, 20)";
    else if (a.type == 1.75)
      canvas.strokeStyle = "rgb(240, 50, 20)";
    else if (a.type == 1.5)
      canvas.strokeStyle = "rgb(240, 100, 20)";
    else if (a.type == 1.25)
      canvas.strokeStyle = "rgb(240, 150, 20)";
    else if (a.type == 1 || a.type == 0 || a.type == 3 || a.type == 4) 
   	  canvas.strokeStyle = "rgb(240, 200, 20)";
  }

  canvas.beginPath();
  canvas.moveTo(x, y);

  for (var j = 0; j < 5; ++j) {
    var d = a.d[j];
    var x = a.cr.x + d.x * rcos - d.y * rsin;
    var y = a.cr.y + d.x * rsin + d.y * rcos;

    canvas.lineTo(x, y);
  }

  canvas.closePath();
  canvas.stroke();
}

function game_simulate() {
  var _ph = ph;

  if (_game_mode == 0)
    _ph.step(_ph.time_per_frame * 1.5);
  else
    _ph.step_playground(_ph.time_per_frame * 1.5);

  var canvas = $("#game-canvas").get(0).getContext("2d");

  canvas.fillStyle = "rgba(0, 0, 0, 0.45)";
  canvas.fillRect (0, 0, 320, 320);

  var u = _ui.sprites;  

  // Update score.
  if (_game_mode == 0) {
    if (_ph.current_score < _ph.score) {
      _ph.current_score += 25;
      if (_ph.current_score > _ph.score) 
        _ph.current_score = _ph.score;
      $("#game-score").get(0).innerHTML = "wave " + (_stage + 1) + " - " + _ph.current_score;
    }
    if (_ph.current_score > _ph.score) {
      _ph.current_score -= 25;
      if (_ph.current_score < _ph.score) 
        _ph.current_score = _ph.score;
      $("#game-score").get(0).innerHTML = "wave " + (_stage + 1) + " - " + _ph.current_score
    }
  }

  // Draw touch circles.
  canvas.lineWidth = 2;
  for (var i = 0; i < _ph.circles.length; ++i) {
    var c = _ph.circles[i];

    canvas.strokeStyle = c.color + 0.5 * (1 - c.r / c.target) + ")";
    canvas.beginPath();
    canvas.arc(c.x, c.y, c.r, 0, Math.PI * 2, false);
    canvas.closePath();
    canvas.stroke();
  }

  // Draw powerup.
  if (_ph.powerup_type != 0 && _ph.powerup_ready == 0) {
    canvas.lineWidth = 2;
    if (_ph.powerup_type == 1) {
      canvas.beginPath();
      canvas.arc(_ph.powerup_c.x, _ph.powerup_c.y, 8, 0, Math.PI * 2, false);
      canvas.closePath();
      canvas.strokeStyle = "rgba(255, 255, 255, 0.8)";
      canvas.stroke();

      canvas.beginPath();
      canvas.moveTo(_ph.powerup_c.x - 5, _ph.powerup_c.y);
      canvas.lineTo(_ph.powerup_c.x + 5, _ph.powerup_c.y);
      canvas.moveTo(_ph.powerup_c.x, _ph.powerup_c.y - 5);
      canvas.lineTo(_ph.powerup_c.x, _ph.powerup_c.y + 5);
      canvas.closePath();
      canvas.strokeStyle = "rgb(255, 0, 0)";
      canvas.stroke();
    } else if (_ph.powerup_type == 2) {
      canvas.beginPath();
      canvas.arc(_ph.powerup_c.x, _ph.powerup_c.y, 8, 0, Math.PI * 2, false);
      canvas.closePath();
      canvas.strokeStyle = "rgba(255, 255, 255, 0.8)";
      canvas.stroke();

      canvas.beginPath();
      canvas.moveTo(_ph.powerup_c.x - 5, _ph.powerup_c.y);
      canvas.lineTo(_ph.powerup_c.x + 5, _ph.powerup_c.y);
      canvas.moveTo(_ph.powerup_c.x, _ph.powerup_c.y - 5);
      canvas.lineTo(_ph.powerup_c.x, _ph.powerup_c.y + 5);
      canvas.moveTo(_ph.powerup_c.x - 4, _ph.powerup_c.y - 4);
      canvas.lineTo(_ph.powerup_c.x + 4, _ph.powerup_c.y + 4);
      canvas.moveTo(_ph.powerup_c.x + 4, _ph.powerup_c.y - 4);
      canvas.lineTo(_ph.powerup_c.x - 4, _ph.powerup_c.y + 4);
      canvas.closePath();
      canvas.strokeStyle = "rgb(0, 128, 255)";
      canvas.stroke();
    } else if (_ph.powerup_type == 3) {
      if (_ph.powerup_strength > 0) {
         
        if (_ph.ship && _ph.health > 0) {
          var s = _ph.ship;
          canvas.beginPath();
          canvas.arc(s.c.x, s.c.y, 19, 0, Math.PI * 2, false);
          canvas.closePath();
          canvas.strokeStyle = "rgba(64, 200, 64, 0.7)";
          canvas.stroke();
        }
      } else {
        canvas.beginPath();
        canvas.arc(_ph.powerup_c.x, _ph.powerup_c.y, 8, 0, Math.PI * 2, false);
        canvas.closePath();
        canvas.strokeStyle = "rgba(255, 255, 255, 0.8)";
        canvas.stroke();

        canvas.beginPath();
        canvas.arc(_ph.powerup_c.x, _ph.powerup_c.y, 5, 0, Math.PI * 2, false);      
        canvas.closePath();
        canvas.strokeStyle = "rgb(64, 200, 64)";
        canvas.stroke();
      }
    } else if (_ph.powerup_type == 4) {
      if (_ph.powerup_strength > 0) {
      } else {
        canvas.beginPath();
        canvas.arc(_ph.powerup_c.x, _ph.powerup_c.y, 8, 0, Math.PI * 2, false);
        canvas.closePath();
        canvas.strokeStyle = "rgba(255, 255, 255, 0.8)";
        canvas.stroke();

        canvas.beginPath();
        canvas.moveTo(_ph.powerup_c.x, _ph.powerup_c.y - 4);
        canvas.lineTo(_ph.powerup_c.x, _ph.powerup_c.y + 4);
        canvas.moveTo(_ph.powerup_c.x + 3, _ph.powerup_c.y - 4);
        canvas.lineTo(_ph.powerup_c.x + 3, _ph.powerup_c.y + 4);
        canvas.moveTo(_ph.powerup_c.x - 3, _ph.powerup_c.y - 4);
        canvas.lineTo(_ph.powerup_c.x - 3, _ph.powerup_c.y + 4);
        canvas.closePath();
        canvas.strokeStyle = "rgb(0, 128, 255)";
        canvas.stroke();
      }
    }
  }

  // Draw ship.
  if (_game_mode == 0) {
    canvas.lineWidth = 3;

    if (_ph.ship && _ph.health > 0) {
      var s = _ph.ship;
      canvas.strokeStyle = s.color;    
      var rcos = Math.cos(s.r);
      var rsin = Math.sin(s.r);
      var x = s.c.x + s.d[2].x * rcos - s.d[2].y * rsin;
      var y = s.c.y + s.d[2].x * rsin + s.d[2].y * rcos;

      canvas.beginPath();
      canvas.moveTo(x, y);

      for (var j = 0; j < 2; ++j) {
        var d = s.d[j];
        var x = s.c.x + d.x * rcos - d.y * rsin;
        var y = s.c.y + d.x * rsin + d.y * rcos;

        canvas.lineTo(x, y);
      }

      canvas.closePath();
      canvas.stroke();
    }
  }

  // Draw asteroids.
  for (var i = 0; i < _ph.asteroids.length; ++i) {
    var a = _ph.asteroids[i];

    draw_asteroid(canvas, a);
  }

  // Check for the next wave.
  if (_game_mode == 0 && _ph.asteroids == 0) {
    if (_ph.health > 0) {
      ++_wave_counter;

      var max_counter = _stage > 14 ? 0 : 60;

      if (_wave_counter >= max_counter) {
        _wave_counter = 0;
        ++_stage;
        
        var current_stage = _stage;
        if (_stage > 14) {
          _stage = Math.floor(Math.random() * 6) + 9;
        }

        if (_stage == 1) {
          var rnd_properties = game_random_position(15);
          ph.create_asteroid(rnd_properties.c, rnd_properties.v, rnd_properties.r, 10, 20, 1);
          var rnd_properties = game_random_position(25);
          ph.create_asteroid(rnd_properties.c, rnd_properties.v, rnd_properties.r, 10, 20, 1);
        } else if (_stage == 2) {
          var rnd_properties = game_random_position(15);
          ph.create_asteroid(rnd_properties.c, rnd_properties.v, rnd_properties.r, 10, 20, 1);
          var rnd_properties = game_random_position(25);
          ph.create_asteroid(rnd_properties.c, rnd_properties.v, rnd_properties.r, 10, 20, 1);
          var rnd_properties = game_random_position(30);
          ph.create_asteroid(rnd_properties.c, rnd_properties.v, rnd_properties.r, 10, 20, 1);
        } else if (_stage == 3) {
          var rnd_properties = game_random_position(20);
          ph.create_asteroid(rnd_properties.c, rnd_properties.v, rnd_properties.r, 10, 20, 2);
        } else if (_stage == 4) {
          var rnd_properties = game_random_position(20);
          ph.create_asteroid(rnd_properties.c, rnd_properties.v, rnd_properties.r, 10, 20, 2);
          var rnd_properties = game_random_position(20);
          ph.create_asteroid(rnd_properties.c, rnd_properties.v, rnd_properties.r, 10, 20, 2);
          var rnd_properties = game_random_position(20);
          ph.create_asteroid(rnd_properties.c, rnd_properties.v, rnd_properties.r, 10, 20, 2);
        } else if (_stage == 5) {
          var rnd_properties = game_random_position(25, 45);
          ph.create_asteroid_2(rnd_properties.c, rnd_properties.v, rnd_properties.r, 35, 0, 2, 10, 15, 1);  
          ph.create_asteroid_2(rnd_properties.c, rnd_properties.v, rnd_properties.r, 35, Math.PI / 2, 2, 10, 15, 1);  
          ph.create_asteroid_2(rnd_properties.c, rnd_properties.v, rnd_properties.r, 35, Math.PI, 2, 10, 15, 1);  
          ph.create_asteroid_2(rnd_properties.c, rnd_properties.v, rnd_properties.r, 35, Math.PI * 3 / 2, 2, 10, 15, 1);
        } else if (_stage == 6) {
          var rnd_properties = game_random_position(15);
          ph.create_asteroid(rnd_properties.c, rnd_properties.v, rnd_properties.r, 10, 10, 3);
        } else if (_stage == 7) {
          var v = 25;
          if (_free_flight == 0)
            v = 15;
          var rnd_properties = game_random_position(v, 8);
          ph.create_asteroid(rnd_properties.c, rnd_properties.v, rnd_properties.r, 10, 24, 2);
          ph.create_asteroid_2(rnd_properties.c, rnd_properties.v, rnd_properties.r, 10, 0, 2, 10, 8, 0);  
          ph.create_asteroid_2(rnd_properties.c, rnd_properties.v, rnd_properties.r, 10, Math.PI / 3, 2, 10, 8, 0);  
          ph.create_asteroid_2(rnd_properties.c, rnd_properties.v, rnd_properties.r, 10, Math.PI * 2 / 3, 2, 10, 8, 0);  

          var rnd_properties = game_random_position(v, 8);
          ph.create_asteroid(rnd_properties.c, rnd_properties.v, rnd_properties.r, 10, 24, 2);
          ph.create_asteroid_2(rnd_properties.c, rnd_properties.v, rnd_properties.r, 10, 0, 2, 10, 8, 0);  
          ph.create_asteroid_2(rnd_properties.c, rnd_properties.v, rnd_properties.r, 10, Math.PI / 3, 2, 10, 8, 0);  
          ph.create_asteroid_2(rnd_properties.c, rnd_properties.v, rnd_properties.r, 10, Math.PI * 2 / 3, 2, 10, 8, 0);  

          var rnd_properties = game_random_position(v, 8);
          ph.create_asteroid(rnd_properties.c, rnd_properties.v, rnd_properties.r, 10, 24, 2);
          ph.create_asteroid_2(rnd_properties.c, rnd_properties.v, rnd_properties.r, 10, 0, 2, 10, 8, 0);  
          ph.create_asteroid_2(rnd_properties.c, rnd_properties.v, rnd_properties.r, 10, Math.PI / 3, 2, 10, 8, 0);  
          ph.create_asteroid_2(rnd_properties.c, rnd_properties.v, rnd_properties.r, 10, Math.PI * 2 / 3, 2, 10, 8, 0);  
        } else if (_stage == 8) {
          var v = 20;
          if (_free_flight == 0)
            v = 20;

          var rnd_properties = game_random_position(20);
          ph.create_asteroid(rnd_properties.c, rnd_properties.v, rnd_properties.r, 10, 20, 2);
          var rnd_properties = game_random_position(20);
          ph.create_asteroid(rnd_properties.c, rnd_properties.v, rnd_properties.r, 10, 20, 2);
          var rnd_properties = game_random_position(20);
          ph.create_asteroid(rnd_properties.c, rnd_properties.v, rnd_properties.r, 10, 20, 2);
          var rnd_properties = game_random_position(20);
          ph.create_asteroid(rnd_properties.c, rnd_properties.v, rnd_properties.r, 10, 20, 2);
        } else if (_stage == 9) {
          var rnd_properties = game_random_position(20);
          ph.create_asteroid(rnd_properties.c, rnd_properties.v, rnd_properties.r, 10, 10, 5);
          var rnd_properties = game_random_position(20);
          ph.create_asteroid(rnd_properties.c, rnd_properties.v, rnd_properties.r, 10, 10, 5);
          var rnd_properties = game_random_position(20);                               
          ph.create_asteroid(rnd_properties.c, rnd_properties.v, rnd_properties.r, 10, 10, 5);
        } else if (_stage == 10) {
          var rnd_properties = game_random_position(25);
          ph.create_asteroid(rnd_properties.c, rnd_properties.v, rnd_properties.r, 10, 10, 4);
        } else if (_stage == 11) {
          var rnd_properties = game_random_position(25);
          ph.create_asteroid(rnd_properties.c, rnd_properties.v, rnd_properties.r, 10, 10, 0);
          var rnd_properties = game_random_position(25);
          ph.create_asteroid(rnd_properties.c, rnd_properties.v, rnd_properties.r, 10, 10, 0);
          var rnd_properties = game_random_position(25);
          ph.create_asteroid(rnd_properties.c, rnd_properties.v, rnd_properties.r, 10, 10, 0);

          var v = 90;
          if (_free_flight == 0)
            v = 60;

          var rnd_properties = game_random_position(v);
          ph.create_asteroid(rnd_properties.c, rnd_properties.v, rnd_properties.r, 10, 10, 0);
          var rnd_properties = game_random_position(v);
          ph.create_asteroid(rnd_properties.c, rnd_properties.v, rnd_properties.r, 10, 10, 0);
          var rnd_properties = game_random_position(v);
          ph.create_asteroid(rnd_properties.c, rnd_properties.v, rnd_properties.r, 10, 10, 0);

          var rnd_properties = game_random_position(v);
          ph.create_asteroid(rnd_properties.c, rnd_properties.v, rnd_properties.r, 10, 10, 0);
          var rnd_properties = game_random_position(v);
          ph.create_asteroid(rnd_properties.c, rnd_properties.v, rnd_properties.r, 10, 10, 0);
          var rnd_properties = game_random_position(v);
          ph.create_asteroid(rnd_properties.c, rnd_properties.v, rnd_properties.r, 10, 10, 0);
        } else if (_stage == 12) {
          var rnd_properties = game_random_position(20);
          ph.create_asteroid(rnd_properties.c, rnd_properties.v, rnd_properties.r, 10, 20, 6);
          var rnd_properties = game_random_position(20);
          ph.create_asteroid(rnd_properties.c, rnd_properties.v, rnd_properties.r, 10, 20, 6);
          var rnd_properties = game_random_position(20);                               
          ph.create_asteroid(rnd_properties.c, rnd_properties.v, rnd_properties.r, 10, 20, 6);
        } else if (_stage == 13) {
          var rnd_properties = game_random_position(35, 30);
          ph.create_asteroid(rnd_properties.c, rnd_properties.v, rnd_properties.r, 10, 40, 2);
          ph.create_asteroid_2(rnd_properties.c, rnd_properties.v, rnd_properties.r, 20, 0, 3, 10, 8, 0);  
          ph.create_asteroid_2(rnd_properties.c, rnd_properties.v, rnd_properties.r, 20, Math.PI / 2, 3, 10, 8, 0);  
          ph.create_asteroid_2(rnd_properties.c, rnd_properties.v, rnd_properties.r, 20, Math.PI, 3, 10, 8, 0);  
          ph.create_asteroid_2(rnd_properties.c, rnd_properties.v, rnd_properties.r, 20, Math.PI * 3 / 2, 3, 10, 8, 0);

          var rnd_properties = game_random_position(35, 30);
          ph.create_asteroid(rnd_properties.c, rnd_properties.v, rnd_properties.r, 10, 40, 2);
          ph.create_asteroid_2(rnd_properties.c, rnd_properties.v, rnd_properties.r, 20, 0, 3, 10, 8, 0);  
          ph.create_asteroid_2(rnd_properties.c, rnd_properties.v, rnd_properties.r, 20, Math.PI / 2, 3, 10, 8, 0);  
          ph.create_asteroid_2(rnd_properties.c, rnd_properties.v, rnd_properties.r, 20, Math.PI, 3, 10, 8, 0);  
          ph.create_asteroid_2(rnd_properties.c, rnd_properties.v, rnd_properties.r, 20, Math.PI * 3 / 3, 2, 10, 8, 0);

          if (_free_flight == 1) {
            var rnd_properties = game_random_position(35, 30);
            ph.create_asteroid(rnd_properties.c, rnd_properties.v, rnd_properties.r, 10, 40, 2);
            ph.create_asteroid_2(rnd_properties.c, rnd_properties.v, rnd_properties.r, 20, 0, 3, 10, 8, 0);  
            ph.create_asteroid_2(rnd_properties.c, rnd_properties.v, rnd_properties.r, 20, Math.PI / 2, 3, 10, 8, 0);  
            ph.create_asteroid_2(rnd_properties.c, rnd_properties.v, rnd_properties.r, 20, Math.PI, 3, 10, 8, 0);  
            ph.create_asteroid_2(rnd_properties.c, rnd_properties.v, rnd_properties.r, 20, Math.PI * 3 / 3, 2, 10, 8, 0);
          }
        } else if (_stage == 14) {
          var rnd_properties = game_random_position(35);
          ph.create_asteroid(rnd_properties.c, rnd_properties.v, rnd_properties.r, 10, 20, 1);
          var rnd_properties = game_random_position(45);
          ph.create_asteroid(rnd_properties.c, rnd_properties.v, rnd_properties.r, 10, 20, 1);
          var rnd_properties = game_random_position(50);
          ph.create_asteroid(rnd_properties.c, rnd_properties.v, rnd_properties.r, 10, 20, 1);
          var rnd_properties = game_random_position(35);
          ph.create_asteroid(rnd_properties.c, rnd_properties.v, rnd_properties.r, 10, 20, 1);
          var rnd_properties = game_random_position(25);
          ph.create_asteroid(rnd_properties.c, rnd_properties.v, rnd_properties.r, 10, 20, 1);
          var rnd_properties = game_random_position(50);          
          ph.create_asteroid(rnd_properties.c, rnd_properties.v, rnd_properties.r, 10, 20, 1);
          if (_free_flight == 1) {
            var rnd_properties = game_random_position(35);
            ph.create_asteroid(rnd_properties.c, rnd_properties.v, rnd_properties.r, 10, 20, 1);
            var rnd_properties = game_random_position(45);
            ph.create_asteroid(rnd_properties.c, rnd_properties.v, rnd_properties.r, 10, 20, 1);
            var rnd_properties = game_random_position(50);
            ph.create_asteroid(rnd_properties.c, rnd_properties.v, rnd_properties.r, 10, 20, 1);
          }
        }

        _stage = current_stage;
      }
    }
  }

  // Draw attractors.
  for (var i = _ph.attractors.length - 1; i >= 0; --i) {  
    var a = _ph.attractors[i];

    if (a.time_to_live > a.lower_bound)
      canvas.globalAlpha = 1;
    else
      canvas.globalAlpha = a.time_to_live / a.lower_bound;

    canvas.drawImage(_img_combined_obj,
      u.plus.x, u.plus.y, u.plus.width, u.plus.height, a.x - Math.round(u.plus.width / 2), a.y - Math.round(u.plus.height / 2), u.plus.width, u.plus.height);
  }

  // Draw particles.
  canvas.globalAlpha = 1;
               
  canvas.strokeStyle = _ph.color_a;
  canvas.lineWidth = 2;

  {
    canvas.beginPath();

    for (var i = _ph.capacity - 1; i >= 0; --i) {
      var p = _ph.particles[i];
      if (p.free || p.dead)
        continue;
          
      canvas.moveTo(p.l.x, p.l.y);
      canvas.lineTo(p.c.x, p.c.y);        
    }

    canvas.closePath();
    canvas.stroke();    
  }

  // Draw health.  
  if (_game_mode == 0 && _ph.health > 0) {
    canvas.lineWidth = 2;
    canvas.strokeStyle = "rgba(0, 128, 255, 0.6)";
    canvas.beginPath();
    var x = 162 - Math.floor(3 * _ph.max_health / 2);
    for (var i = 0; i < _ph.health; ++i, x += 3) {
      canvas.moveTo(x, 2);
      canvas.lineTo(x, 8);
    }
    canvas.closePath();
    canvas.stroke();

    canvas.strokeStyle = "rgba(255, 64, 0, 0.2)";
    canvas.beginPath();
    for (var i = _ph.health < 0 ? 0 : _ph.health; i < _ph.max_health; ++i, x += 3) {
      canvas.moveTo(x, 2);
      canvas.lineTo(x, 8);
    }
    canvas.closePath();
    canvas.stroke();                                                            
  }

  _simulator_running = setTimeout(game_simulate, _ph.time_per_frame_milliseconds * _ph.multiplier);
}

// Game end.
function game_end() {
  $("#grats-score").get(0).innerHTML = "Your score " + ph.score;

  $("#grats-container").css("display", "block").css("opacity", "0").animate({opacity: 1}, 400);
}

// Preloading and initialization.
function make_img(v, force) {
  return "imgs/" + v;
}

var _img_combined = make_img("ui.atlas.optimized.png");
var _img_combined_obj;

var _preload_init = [ "imgs/underclouds.png", "imgs/back.jpg", "imgs/help1.png", "imgs/help2.png" ];
var _preload_continue = [ _img_combined ];
var _preload_continue_count = _preload_continue.length;

function fixup_logo() {
  $("#logo-inner").css("background", "transparent url(imgs/back.jpg) no-repeat center");
}

function set_image(id, style) {
  return $(id).attr("width", style.width).attr("height", style.height).css("background",
    "transparent url(" + _img_combined + ") no-repeat " + style.style);
}

function fixup_preload() {
  _img_combined_obj = new Image();
  _img_combined_obj.src = _img_combined;

  set_image("#btn-free-flight", _ui.sprites.free_flight).css("left", "20px").css("top", "105px");
  set_image("#btn-touch-flight", _ui.sprites.touch_flight).css("left", "122px").css("top", "98px");
  set_image("#btn-star-defense", _ui.sprites.star_defense).css("left", "217px").css("top", "105px");
  set_image("#btn-scores-free", _ui.sprites.hiscores).css("left", "20px").css("top", "200px");
  set_image("#btn-scores-touch", _ui.sprites.hiscores).css("left", "122px").css("top", "200px");
  set_image("#btn-scores-defense", _ui.sprites.hiscores).css("left", "223px").css("top", "200px");
  set_image("#btn-help", _ui.sprites.howto).css("left", "96px").css("top", "247px");
  set_image("#btn-playground", _ui.sprites.playground).css("left", "57px").css("top", "283px");
  set_image("#btn-more", _ui.sprites.more).css("left", "92px").css("top", "323px");

  set_image("#grats", _ui.sprites.over);
  set_image("#btn-go", _ui.sprites.continue_img);

  set_image("#btn-scores-continue", _ui.sprites.continue_img);
}  

function mouse_position(current, event) {
  var x = 0, y = 0;
  for (; current != null; current = current.offsetParent) {
    x += current.offsetLeft;
    y += current.offsetTop;
  }
 
 return {x: event.pageX - x, y: event.pageY - y };
}

function process_scores(xml) {
  var sb = new StringBuilder();

  sb.appendList("<div class='scores-header'>All Time Best</div>");

  $("a > v", xml).each(function() {
    var n = $("<div/>").text($(this).attr("n")).html();

    sb.appendList("<div class='score-line'><div class='name'>", 
      n, "</div><div><span class='score'>",
      $(this).attr("t"), "</span></div></div>");
  });

  $("#scores-all").get(0).innerHTML = sb.toString();

  sb = new StringBuilder();

  sb.appendList("<div class='scores-header'>Today's Best</div>");

  $("q > v", xml).each(function() {
    var n = $("<div/>").text($(this).attr("n")).html();

    sb.appendList("<div class='score-line'><div class='name'>", 
      n, "</div><div><span class='score'>",
      $(this).attr("t"), "</span></div></div>");
  });

  $("#scores-today").get(0).innerHTML = sb.toString();

  $("#scores-loading").css("display", "none");
  $("#scores-table").css("display", "block");
}

function setup_click_handlers() {
  initTouchHandlers();

  var _ph = ph;

  $("#btn-free-flight").click(function() {
    _game_mode = 0;
    _free_flight = 1;
    _touch_mode = 0;
   
    game_start();

    return false;
  });

  $("#btn-touch-flight").click(function() {
    _game_mode = 0;
    _free_flight = 1;
    _touch_mode = 1;
   
    game_start();

    return false;
  });

  $("#btn-star-defense").click(function() {
    _game_mode = 0;
    _free_flight = 0;
    _touch_mode = 0;
   
    game_start();

    return false;
  });

  $("#btn-playground").click(function() {
    _game_mode = 1;
   
    game_start();

    return false;
  });

  $("#btn-menu").click(function() {
    stop_simulator();

    $("#logo").css("opacity", "0").css("display", "block");
    $("#game-container").animate({opacity: 0}, 400, function() {
      $("#game-container").css("display", "none");

      $("#logo").animate({opacity: 1}, 400, function() { });
    });

    return false;
  });

  $("#btn-help").click(function() {

    _help_stage = 0;
    $("#help-outer").css("background", "transparent url(imgs/help1.png) no-repeat 0px 0px");

    $("#help-container").css("opacity", "0").css("display", "block").animate(
      {opacity: 1}, 400);

    return false;
  });

  $("#help-outer").click(function() {
    createCookie("rulesShown", "1", 365);    

    if (_help_stage == 1) {
      $("#help-container").animate({opacity: 0}, 400, function() {
        $("#help-container").css("display", "none");

        if (_help_start_game) {
          start_simulator();

          _help_start_game = false;
        }
      });
    } else {
      _help_stage = 1;      

      $("#help-outer").animate({opacity: 0}, 400, function() {
        $("#help-outer").css("background", "transparent url(imgs/help2.png) no-repeat 0px 0px");

        $("#help-outer").animate({opacity: 1}, 400);
      });
    }

    return false;
  });

  $("#btn-scores-free").click(function() {
    var lastScore = readCookie("lastScore");
    if (lastScore && lastScore.length > 0)
      $("#scores-last").get(0).innerHTML = "Your last score was " + lastScore;
	  else
	    $("#scores-last").get(0).innerHTML = "";

    $("#scores-loading").css("display", "block");
    $("#scores-table").css("display", "none");
    $("#scores-container").css("display", "block").css("opacity", "0").animate({opacity: 1}, 400, function() {

      $.post("score.php", {}, process_scores);

    });

    return false;
  });

  $("#btn-scores-defense").click(function() {
    var lastScore = readCookie("lastScore2");
    if (lastScore && lastScore.length > 0)
      $("#scores-last").get(0).innerHTML = "Your last score was " + lastScore;
	  else
	    $("#scores-last").get(0).innerHTML = "";

    $("#scores-loading").css("display", "block");
    $("#scores-table").css("display", "none");
    $("#scores-container").css("display", "block").css("opacity", "0").animate({opacity: 1}, 400, function() {

      $.post("score2.php", {}, process_scores);

    });

    return false;
  });

  $("#btn-scores-touch").click(function() {
    var lastScore = readCookie("lastScore3");
    if (lastScore && lastScore.length > 0)
      $("#scores-last").get(0).innerHTML = "Your last score was " + lastScore;
	  else
	    $("#scores-last").get(0).innerHTML = "";

    $("#scores-loading").css("display", "block");
    $("#scores-table").css("display", "none");
    $("#scores-container").css("display", "block").css("opacity", "0").animate({opacity: 1}, 400, function() {

      $.post("score3.php", {}, process_scores);

    });

    return false;
  });

  $("#btn-scores-continue").click(function() {
    $("#scores-container").animate({opacity: 0}, 400, function() {
      $("#scores-container").css("display", "none");
    });

    return false;
  });

  $("#btn-go").click(function() {
    stop_simulator();

    // Save scores.
    var name = $("#grats-name").val();

    createCookie("name", name, 365);
    if (_free_flight == 0)
      createCookie("lastScore2", ph.score, 365);
    else {
      if (_touch_mode == 0)
        createCookie("lastScore", ph.score, 365);
      else
        createCookie("lastScore3", ph.score, 365);
    }

    if (ph.score > 0 && name != "nickname") {
      if (_free_flight == 0) {
        $.post("score2.php", 
          { name: name, total: ph.score }, function(xml) {});
      } else {
        if (_touch_mode == 0) {
          $.post("score.php", 
            { name: name, total: ph.score }, function(xml) {});
        } else {
          $.post("score3.php", 
            { name: name, total: ph.score }, function(xml) {});
        }
      }
    }

    $("#game-container").animate({opacity: 0}, 200, function() {
      $("#game-container").css("display", "none");
    });

    $("#grats-container").animate({opacity: 0}, 400, function() {
      $("#grats-container").css("display", "none");

      $("#logo").css("display", "block").css("opacity", "0").animate({opacity: 1}, 400);
    });
   
    return false;
  });

  $("#game-canvas-clicker").mousedown(function(e) {
    if (_touch_mode == 0)
      return;

    var p = mouse_position(this, e);

    begin_touch(p.x, p.y); 
  });                   

  $("#game-canvas-clicker").mousemove(function(e) {
    if (_touch_mode == 0)
      return;

    var p = mouse_position(this, e);

    move_touch(p.x, p.y);
  });
    
  $("#game-canvas-clicker").mouseup(function(e) {
    if (_touch_mode == 0)
      return;

    var p = mouse_position(this, e);

    end_touch(p.x, p.y);
  });

  $("#game-canvas-clicker").click(function(event) {
    if (_touch_mode == 1)
      return;

    var c = mouse_position(this, event);

    var found = false; 

    if (_game_mode == 0 && _free_flight == 1 && _touch_mode == 0) {
      for (var i = 0; i < _ph.attractors.length; ++i) {
        var d = v_dist_sq(_ph.attractors[i], c);      

        if (d < 900) {
          _ph.attractors[i] = _ph.attractors[0];
          _ph.attractors.shift();

          break;
        }
      }
    }

    if (!found && _ph.attractors.length < 4) {
      var a = _ph.add_attractor(c.x, c.y, 100);
      _ph.add_circle(c.x, c.y, 40, "rgba(100, 150, 255,");

      a.time_to_live = _game_mode == 0 ? (_free_flight ? 2 : 0.5) : 10;
      a.lower_bound = _game_mode == 0 ? (_free_flight ? 1 : 0.25) : 5;
      a.decrease = 0;

      if (_game_mode == 0 && _free_flight == 0) {
        var s = _ph.ship;

        s.r_target = Math.atan2(c.y - 160, c.x - 160) - Math.PI / 2;

        if (s.r_target - s.r > Math.PI) 
          s.r += Math.PI * 2;
        else if (s.r_target - s.r < - Math.PI)
          s.r -= Math.PI * 2;
      }
    }
  });
}

$(function() {
  // Begin images preloading.
  $(document.createElement('img')).bind('load', function() {
    if(_preload_init[0]) {
      this.src = _preload_init.shift();
    } else {
      $("#preloading").css("display", "none");

      fixup_logo();

      $("#brand").animate({opacity: 1}, 1500, function() {
        setTimeout(function() {
          $("#brand").animate({opacity: 0}, 1500, function() {
            $("#logo").animate({opacity: 1}, 1000, function() {
              $("#brand").css("display", "none");
            });
          });
        }, 2000);
      });
        
      $(document.createElement('img')).bind('load', function() {
        if(_preload_continue[0]) {
          this.src = _preload_continue.shift();
        } else {
          fixup_preload();          
          
          $("#logo-loading").css("display", "none");
          $("#logo-loaded").css("display", "inline");

          setTimeout(function() {
            window.scrollTo(0, 1);
          }, 300);
        }
      }).trigger('load');
    }
  }).trigger('load');

  setup_click_handlers();

  var agent = navigator && navigator.userAgent ? navigator.userAgent.toLowerCase() : "";
  var is_iphone = agent && (agent.indexOf('iphone') != -1 || agent.indexOf('ipod') != -1);

  if (is_iphone) {
    ph.multiplier = 0.5;
  }

  if (!is_iphone) {
    $("#pc-padding").css("display", "block");
  }

  var name = readCookie("name");

  if (name && name.length > 0)
    $("#grats-name").val(name);  
  else
    $("#grats-name").val("nickname");

  
});