<!-- spctrvl.html - Space Travel video game -->
<!--
    Programming Notes

    Next tasks:
    - calibrate thrust velocity change with tick length
    - scale acceleration to zoom level
    - gravity
-->
<!DOCTYPE html>
<html>
  <head>
    <title>Space Travel</title>
    <meta charset="utf-8">
    <meta name="description"
	  content="Space Travel video game">

<!-- [STYLE] ======================================================= -->
    <style>
      body {
	  color: #009000 ;
	  background-color: black ;
	  font-family: "Bitstream Vera Sans Mono", "Lucida Sans",
		       "Lucida Console", "MS Gothic" ; 
      }
      canvas {
	  border: 1px solid #909090;
	  margin: auto;
      }
      button {
	  width: 100%;
      }
    </style>
    
  </head>

  <body>
<!-- [CONTENT] ===================================================== -->

<h1>Space Travel</h1>

<canvas id="canvas" width="600" height="600"></canvas>

<div id="controls">
  <table>
    <tr>
      <td><button onmousedown="zoom(1)" onmouseup="zoom(0)"
		  ontouchstart="zoom(1)">ZOOM +<br>Z</button></td>
      <td><button onmousedown="reset()"
		  ontouchstart="reset()">RESET<br>R</button></td>
      <td></td>
      <td><button onmousedown="thrust(1)" onmouseup="thrust(0)"
		  ontouchstart="thrust(1)">&uarr;<br>THRUST</button></td>
    </tr>
    <tr>
      <td><button onmousedown="zoom(-1)" onmouseup="zoom(0)"
		  ontouchstart="zoom(-1)">ZOOM -<br>X</button></td>
      <td><button onmousedown="pause()"
		  ontouchstart="pause()">PAUSE<br>P</button></td>
      <td><button onmousedown="rotate(1)" onmouseup="rotate(0)"
		  ontouchstart="rotate(1)">ROT. L.<br>&larr;</button></td>
      <td><button onmousedown="thrust(-1)" onmouseup="thrust(0)"
		  ontouchstart="thrust(-1)">RETRO<br>&darr;</button></td>
      <td><button onmousedown="rotate(-1)" onmouseup="rotate(0)"
		  ontouchstart="rotate(-1)">ROT. R.<br>&rarr;</button></td>
    </tr>
  </table>
</div>

<!-- [SCRIPT] ====================================================== -->
<script>

  // Earth data
  var EARTHX = 0,          // x position (km)
      EARTHY = 0,          // y position (km)
      EARTHRADIUS = 6378,  // radius (km)
      EARTHMASS = 5.97,    // mass (* 10^24 kg)
      EARTHCOLOR = "#009000";

  // Key codes
  var KEYLARR = 37,
      KEYUARR = 38,
      KEYRARR = 39,
      KEYDARR = 40,
      KEYP =    80,
      KEYR =    82,
      KEYX =    88,
      KEYZ =    90;

  // Other constants
  var PI2 = 2 * Math.PI,
      TICK = 10;       // ms

  // Ship initial status
  var SHIPX0 = EARTHRADIUS, // km - On X-axis at Earth's surface
      SHIPY0 = 0,
      SHIPVX0 = 0,
      SHIPVY0 = 0,
      SHIPHEAD0 = 0;

  // Other initial values
  var SCALE0 = 1000;
  
  var CANVAS = document.getElementById("canvas");
  var CTX = CANVAS.getContext("2d");
  var ZOOM = 0;
  var HEADINCR = Math.PI / 200;

  // Ship status
  var shipX = 6378, // x position (km)
      shipY = 0,    // y position (km)
      shipVx = 0,   // x velocity (km/s)
      shipVy = 0,   // y velocity (km/s)
      shipHead = 0, // heading (radian)
      shipColor = "#00ff00",
      shipThrust = 0,
      shipRotate = 0;
  
  // Other status
  var keyin = false,
      paused = false,
      scale = 1000, // canvas width in km
      timer;

  function xformPx (x, y) {
      var d = Math.sqrt (Math.pow (x - shipX, 2) +
			 Math.pow (y - shipY, 2));
      var phi = shipHead + Math.atan ((y - shipY) / (x - shipX));
      var dPx = scalePx (d);
      var xC = -1 * dPx * Math.sin (phi);
      var yC = dPx * Math.cos (phi);
      return [xC, yC];
  }

  function pause () {
      if (paused) {
	  timer = setInterval (update, TICK);
	  paused = false;
      }
      else {
	  clearInterval (timer);
	  paused = true;
      }
  }

  function reset () {
      shipX = SHIPX0;
      shipY = SHIPY0;
      shipVx = SHIPVX0;
      shipVy = SHIPVY0;
      shipHead = SHIPHEAD0;
      scale = SCALE0;
  }
      
  function scalePx (km) {
      return km * CANVAS.width / scale;
  }

  function thrust (dir) {
      shipThrust = dir;
  }

  function rotate (dir) {
      shipRotate = dir;
  }

  function zoom (dir) {
      ZOOM = dir;
  }

  function changeHead (dir) {
      shipHead += (dir * HEADINCR);
      if (shipHead >= PI2) shipHead -= PI2;
      else if (shipHead < 0) shipHead += PI2;
  }

  function drawShip () {
      CTX.beginPath ();
      CTX.moveTo (-5, 0);
      CTX.lineTo (5, 0);
      CTX.lineTo (0, -10);
      CTX.closePath ();
      CTX.fillStyle = shipColor;
      CTX.fill ();
  }

  function drawBody (x, y, r, color) {
      var cXY = xformPx (x, y);
      CTX.beginPath ();
      CTX.arc (cXY[0], cXY[1], scalePx (r), 0, 2*Math.PI);
      CTX.strokeStyle = color;
      CTX.stroke ();
  }
1
  function handleKeyDown (event) {
//      alert ("handleKeyDown ");
      if (!event) { event = window.event; }
      var keycode = event.keyCode || event.which; 
//      alert ("handleKeyDown " + keycode);
      switch (keycode) {
      case KEYLARR:
	  rotate (1);
	  break;
      case KEYUARR:
	  thrust (1);
	  break;
      case KEYRARR:
	  rotate (-1);
	  break;
      case KEYDARR:
	  thrust (-1);
	  break;
      case KEYP:
	  pause ();
	  break;
      case KEYR:
	  reset ();
	  break;
      case KEYX:
	  zoom (-1);
	  break;
      case KEYZ:
	  zoom (1);
	  break;
      }
  }

  function handleKeyUp (event) {
//      alert ("handleKeyUp ");
      if (!event) { event = window.event; }
      var keycode = event.keyCode || event.which; 
//      alert ("handleKeyUp " + keycode);
      switch (keycode) {
      case KEYLARR:
      case KEYRARR:
	  rotate (0);
	  break;
      case KEYUARR:
      case KEYDARR:
	  thrust (0);
	  break;
      case KEYX:
      case KEYZ:
	  zoom (0);
	  break;
      }
  }

  function update () {
//      if (keyin) alert ("keyin " + keyin);
      if (ZOOM > 0) scale *= 0.99;
      if (ZOOM < 0) scale *= 1.01;
      changeHead (shipRotate);
      shipVx += shipThrust * Math.cos (shipHead);
      shipVy += -1 * shipThrust * Math.sin (shipHead);
      shipX += shipVx;
      shipY += shipVy;
      CTX.clearRect (-1 * CANVAS.width / 2, -1 * CANVAS.height / 2,
		     CANVAS.width, CANVAS.height);
      drawBody (EARTHX, EARTHY, EARTHRADIUS, EARTHCOLOR);
      drawShip ();
  }
      
  // Main process

  CTX.translate (CANVAS.width/2, CANVAS.height/2);

  if (window.addEventListener) {
      document.addEventListener("keydown", handleKeyDown, false);
      document.addEventListener("keyup", handleKeyUp, false);
  }
  else {
      document.attachEvent("onkeydown", handleKeyDown);
      document.attachEvent("onkeyup", handleKeyUp);
  }
  timer = setInterval (update, TICK);


</script>

  </body>
</html>