/*
  agenderoids.c
  
  An asteroid shooting game with vector graphics for the Agenda VR3
  Linux-based PDA.
  
  by Bill Kendrick
  bill@newbreedsoftware.com
  http://www.newbreedsoftware.com/agendaroids/
  
  May 18, 2001 - March 7, 2002
*/

// MAKE MACHINE DEPENDENT CHANGES HERE:
//#include <vectrex/bios.h>
#include <stdio.h> /* remove these 2 includes on a vector display like the Vectrex */
#include <string.h>
// replace with something appropriate...
void newframe(void) { fprintf(stdout, "newframe();\n"); } // may need to use to sync to constant frame rate...
void line_abs(int fx, int fy, int tx, int ty) { fprintf(stdout, "line(%d,%d, %d,%d);\n", fx, fy, tx, ty); }
void get_buttons(int *a, int *b, int *c, int *d) { *a = *b = *c = *d = 0; }
//

// these avoid us having to pull in stdio etc...
static int rand(void) { // 16-bit
  static int rseed = 1234;
  return (rseed = (rseed * 2053) + 13849) & 0x7FFF;
}
int STRLEN(char *s) { int len = 0; while (*s++ != '\0') len++; return len; }
void STRCPY(char *dest, char *src) { int c; do { c = *src++; *dest++ = c; } while (c != '\0'); }
void pnum(char *target, int num) {    // handles -32768:32767
  int digit, zeroes;
  if (num >= 0) num = -num; else *target++ = '-';
  digit = 0;
  zeroes = 1;
  // max 11 add/subtracts...
  if (num <= -20000) { num += 20000; digit += 2; zeroes = 0; }
  if (num <= -10000) { num += 10000; digit += 1; zeroes = 0; }
  if (!zeroes) *target++ = digit+'0';
  digit = 0;
  if (num <= -8000) { num += 8000; digit += 8; zeroes = 0; } else if (num <= -4000) { num += 4000; digit += 4; zeroes = 0; }
  if (num <= -2000) { num += 2000; digit += 2; zeroes = 0; }
  if (num <= -1000) { num += 1000; digit += 1; zeroes = 0; }
  if (!zeroes) *target++ = digit+'0';
  digit = 0;
  if (num <= -800) { num += 800; digit += 8; zeroes = 0; } else if (num <= -400) { num += 400; digit += 4; zeroes = 0; }
  if (num <= -200) { num += 200; digit += 2; zeroes = 0; }
  if (num <= -100) { num += 100; digit += 1; zeroes = 0; }
  if (!zeroes) *target++ = digit+'0';
  digit = 0;
  if (num <= -80) { num += 80; digit += 8; zeroes = 0; } else if (num <= -40) { num += 40; digit += 4; zeroes = 0; }
  if (num <= -20) { num += 20; digit += 2; zeroes = 0; }
  if (num <= -10) { num += 10; digit += 1; zeroes = 0; }
  if (!zeroes) *target++ = digit+'0';
  *target++ = '0'-num;
}

void sprint_num(char *target, char *text, int param) { STRCPY(target, text); pnum(target+STRLEN(target), param); }

#define ERASE_ALL  /* erase all of screen at once, or one object at a time? */

#define STATE_FORMAT_VERSION "2001.10.09"


#define WIDTH 127
#define HEIGHT 127


/* Constraints: */

#define NUM_BULLETS 4
#define NUM_ASTEROIDS 20
#define NUM_BITS 50
#define AST_RADIUS 5
#define AST_SIDES 6

#define ZOOM_START 20
#define ONEUP_SCORE 10000


/* Types: */

typedef struct letter_type {
  int x, y;
  int xm, ym;
} letter_type;

typedef struct bullet_type {
  int timer;
  int x, y;
  int xm, ym;
} bullet_type;

typedef struct shape_type {
  int radius;
  int angle;
} shape_type;

typedef struct asteroid_type {
  int alive, size;
  int x, y;
  int xm, ym;
  int angle, angle_m;
  shape_type shape[AST_SIDES];
} asteroid_type;

typedef struct bit_type {
  int timer;
  int x, y;
  int xm, ym;
} bit_type;


/* Globals: */

bullet_type bullets[NUM_BULLETS];
asteroid_type asteroids[NUM_ASTEROIDS];
bit_type bits[NUM_BITS];
int sound, buttonmode, want_sound, snd_pitch, snd_dur, text_zoom;
char zoom_str[24];
int x, y, xm, ym, angle;
int player_alive, player_die_timer;
int lives, score, high, level, game_pending;


/* Trig junk:  (thanks to Atari BASIC for this) */

int trig[12] = {
  1024,
  1014,
  984,
  935,
  868,
  784,
  685,
  572,
  448,
  316,
  117,
  0
};


/* Characters: */

int char_vectors[36][5][4] = {
  {
    /* 0 */
    { 0, 0, 1, 0 },
    { 1, 0, 1, 2 },
    { 1, 2, 0, 2 },
    { 0, 2, 0, 0 },
    { -1, -1, -1, -1 }
  },
  
  {
    /* 1 */
    { 1, 0, 1, 2 },
    { -1, -1, -1, -1 },
    { -1, -1, -1, -1 },
    { -1, -1, -1, -1 },
    { -1, -1, -1, -1 }
  },

  {
    /* 2 */
    { 1, 0, 0, 0 },
    { 1, 0, 1, 1 },
    { 0, 1, 1, 1 },
    { 0, 1, 0, 2 },
    { 1, 2, 0, 2 },
  },

  {
    /* 3 */
    { 0, 0, 1, 0 },
    { 1, 0, 1, 2 },
    { 0, 1, 1, 1 },
    { 0, 2, 1, 2 },
    { -1, -1, -1, -1 }
  },

  {
    /* 4 */
    { 1, 0, 1, 2 },
    { 0, 0, 0, 1 },
    { 0, 1, 1, 1 },
    { -1, -1, -1, -1 },
    { -1, -1, -1, -1 }
  },

  {
    /* 5 */
    { 1, 0, 0, 0 },
    { 0, 0, 0, 1 },
    { 0, 1, 1, 1 },
    { 1, 1, 1, 2 },
    { 1, 2, 0, 2 }
  },

  {
    /* 6 */
    { 1, 0, 0, 0 },
    { 0, 0, 0, 2 },
    { 0, 2, 1, 2 },
    { 1, 2, 1, 1 },
    { 1, 1, 0, 1 }
  },

  {
    /* 7 */
    { 0, 0, 1, 0 },
    { 1, 0, 1, 2 },
    { -1, -1, -1, -1 },
    { -1, -1, -1, -1 },
    { -1, -1, -1, -1 }
  },

  {
    /* 8 */
    { 0, 0, 1, 0 },
    { 0, 0, 0, 2 },
    { 1, 0, 1, 2 },
    { 0, 2, 1, 2 },
    { 0, 1, 1, 1 }
  },

  {
    /* 9 */
    { 1, 0, 1, 2 },
    { 0, 0, 1, 0 },
    { 0, 0, 0, 1 },
    { 0, 1, 1, 1 },
    { -1, -1, -1, -1 }
  },

  {
    /* A */
    { 0, 2, 0, 1 },
    { 0, 1, 1, 0 },
    { 1, 0, 1, 2 },
    { 0, 1, 1, 1 },
    { -1, -1, -1, -1 }
  },
  
  {
    /* B */
    { 0, 2, 0, 0 },
    { 0, 0, 1, 0 },
    { 1, 0, 0, 1 },
    { 0, 1, 1, 2 },
    { 1, 2, 0, 2 }
  },

  {
    /* C */
    { 1, 0, 0, 0 },
    { 0, 0, 0, 2 },
    { 0, 2, 1, 2 },
    { -1, -1, -1, -1 },
    { -1, -1, -1, -1 }
  },
  
  {
    /* D */
    { 0, 0, 1, 1 },
    { 1, 1, 0, 2 },
    { 0, 2, 0, 0 },
    { -1, -1, -1, -1 },
    { -1, -1, -1, -1 }
  },
  
  {
    /* E */
    { 1, 0, 0, 0 },
    { 0, 0, 0, 2 },
    { 0, 2, 1, 2 },
    { 0, 1, 1, 1 },
    { -1, -1, -1, -1 }
  },

  {
    /* F */
    { 1, 0, 0, 0 },
    { 0, 0, 0, 2 },
    { 0, 1, 1, 1 },
    { -1, -1, -1, -1 },
    { -1, -1, -1, -1 }    
  },
  
  {
    /* G */
    { 1, 0, 0, 0 },
    { 0, 0, 0, 2 },
    { 0, 2, 1, 2 },
    { 1, 2, 1, 1 },
    { -1, -1, -1, -1 }
  },
  
  {
    /* H */
    { 0, 0, 0, 2 },
    { 1, 0, 1, 2 },
    { 0, 1, 1, 1 },
    { -1, -1, -1, -1 },
    { -1, -1, -1, -1 }
  },
  
  {
    /* I */
    { 1, 0, 1, 2 },
    { -1, -1, -1, -1 },
    { -1, -1, -1, -1 },
    { -1, -1, -1, -1 },
    { -1, -1, -1, -1 }
  },
  
  {
    /* J */
    { 1, 0, 1, 2 },
    { 1, 2, 0, 2 },
    { 0, 2, 0, 1 },
    { -1, -1, -1, -1 },
    { -1, -1, -1, -1 }
  },
  
  {
    /* K */
    { 0, 0, 0, 2 },
    { 1, 0, 0, 1 },
    { 1, 2, 0, 1 },
    { -1, -1, -1, -1 },
    { -1, -1, -1, -1 }
  },
  
  {
    /* L */
    { 0, 0, 0, 2 },
    { 0, 2, 1, 2 },
    { -1, -1, -1, -1 },
    { -1, -1, -1, -1 },
    { -1, -1, -1, -1 }
  },
  
  {
    /* M */
    { 0, 0, 0, 2 },
    { 1, 0, 1, 2 },
    { 0, 0, 1, 1 },
    { 0, 1, 1, 0 },
    { -1, -1, -1, -1 }
  },
  
  {
    /* N */
    { 0, 2, 0, 0 },
    { 0, 0, 1, 2 },
    { 1, 2, 1, 0 },
    { -1, -1, -1, -1 },
    { -1, -1, -1, -1 }
  },
  
  {
    /* O */
    { 0, 0, 1, 0 },
    { 1, 0, 1, 2 },
    { 1, 2, 0, 2 },
    { 0, 2, 0, 0 },
    { -1, -1, -1, -1 }
  },
  
  {
    /* P */
    { 0, 2, 0, 0 },
    { 0, 0, 1, 0 },
    { 1, 0, 1, 1 },
    { 1, 1, 0, 1 },
    { -1, -1, -1, -1 }
  },
  
  { 
    /* Q */
    { 0, 0, 1, 0 },
    { 1, 0, 1, 2 },
    { 1, 2, 0, 2 },
    { 0, 2, 0, 0 },
    { 0, 1, 1, 2 }
  },

  {
    /* R */
    { 0, 2, 0, 0 },
    { 0, 0, 1, 0 },
    { 1, 0, 1, 1 },
    { 1, 1, 0, 1 },
    { 0, 1, 1, 2 }
  },
  
  {
    /* S */
    { 1, 0, 0, 0 },
    { 0, 0, 0, 1 },
    { 0, 1, 1, 1 },
    { 1, 1, 1, 2 },
    { 1, 2, 0, 2 }
  },

  {
    /* T */
    { 0, 0, 1, 0 },
    { 1, 0, 1, 2 },
    { -1, -1, -1, -1 },
    { -1, -1, -1, -1 },
    { -1, -1, -1, -1 }
  },
  
  {
    /* U */
    { 0, 0, 0, 2 },
    { 0, 2, 1, 2 },
    { 1, 2, 1, 0 },
    { -1, -1, -1, -1 },
    { -1, -1, -1, -1 }
  },
  
  {
    /* V */
    { 0, 0, 0, 1 },
    { 0, 1, 1, 2 },
    { 1, 2, 1, 0 },
    { -1, -1, -1, -1 },
    { -1, -1, -1, -1 }
  },
  
  {
    /* W */
    { 0, 0, 0, 2 },
    { 1, 0, 1, 2 },
    { 0, 1, 1, 2 },
    { 0, 2, 1, 1 },
    { -1, -1, -1, -1 }
  },
  
  {
    /* X */
    { 0, 0, 1, 2 },
    { 0, 2, 1, 0 },
    { -1, -1, -1, -1 },
    { -1, -1, -1, -1 },
    { -1, -1, -1, -1 }
  },
  
  {
    /* Y */
    { 0, 0, 1, 1 },
    { 1, 0, 1, 2 },
    { -1, -1, -1, -1 },
    { -1, -1, -1, -1 },
    { -1, -1, -1, -1 }
  },
  
  {
    /* Z */
    { 0, 0, 1, 0 },
    { 1, 0, 0, 2 },
    { 0, 2, 1, 2 },
    { -1, -1, -1, -1 },
    { -1, -1, -1, -1 }
  }
};



/* Local function prototypes: */

int title(void);
int game(void);
void finish(void);
void setup(void);
int fast_cos(int v);
int fast_sin(int v);
void draw_line(int x1, int y1, int x2, int y2);
void draw_segment(int r1, int a1,
		  int r2, int a2,
		  int cx, int cy, int ang);
void add_bullet(int x, int y, int a, int xm, int ym);
void add_asteroid(int x, int y, int xm, int ym, int size);
void add_bit(int x, int y, int xm, int ym);
void draw_asteroid(int size, int x, int y, int angle, shape_type * shape);
void playsound(int pitch, int duration);
void do_playsound(void);
void hurt_asteroid(int j, int xm, int ym, int exp_size);
void add_score(int amount);
void draw_char(char c, int x, int y, int r);
void draw_text(char * str, int x, int y, int s);
void draw_closebox(void);
void draw_buttontoggle(void);
void draw_soundtoggle(void);
void draw_thick_line(int x1, int y1, int x2, int y2);
void reset_level(void);


/* --- MAIN --- */

int left_pressed, right_pressed, up_pressed, shift_pressed;

int main(void)
{
  int done;
  char statefile[256], buf[256];


  setup();


  /* Set defaults: */
  
  score = 0;
  high = 0;
  game_pending = 0;

  /* Main app loop! - not very well structured, because title() and game() are both 'main loop' procedures. */
  
  do
  {
    done = title();

    if (!done)
    {
      done = game();
    }
  }
  while (!done);

  finish();

  return(0);
}


/* Title screen: */

int title(void)
{
  int done, quit;
  int i, snapped, angle, size, counter, x, y, xm, ym, xoff;
  char * titlestr = "AGENDAROIDS";
  char str[20];
  letter_type letters[11];


  /* Reset letters: */

  snapped = 0;
  
  for (i = 0; i < 11; i++)
  {
    letters[i].x = (rand() % 160);
    letters[i].y = (rand() % 240);
    letters[i].xm = 0;
    letters[i].ym = 0;
  }

  xoff = (WIDTH - 160) / 2;

  x = (rand() % WIDTH);
  y = (rand() % HEIGHT);
  xm = (rand() % 4) + 2;
  ym = (rand() % 10) - 5;

  counter = 0; 
  angle = 0;
  size = 40;

  done = 0;
  quit = 0;

  do
  {
    
    counter++;

    newframe();
    get_buttons(&left_pressed, &right_pressed, &up_pressed, &shift_pressed);      
    if (left_pressed || right_pressed || up_pressed || shift_pressed) done = 1;
    
    /* Rotate rock: */
    
    angle = ((angle + 2) % 360);


    /* Make rock grow: */

    if ((counter % 3) == 0)
    {
      if (size > 1)
        size--;
    }

    
    /* Move rock: */

    x = x + xm;

    if (x >= WIDTH)
      x = x - WIDTH;

    y = y + ym;
    
    if (y >= HEIGHT)
      y = y - HEIGHT;
    else if (y < 0)
      y = y + HEIGHT;


    /* Move title characters: */

    if (snapped < 11)
    {
      for (i = 0; i < 11; i++)
      {
        letters[i].x = letters[i].x + letters[i].xm;
        letters[i].y = letters[i].y + letters[i].ym;

      
        /* Home in on final spot! */
      
        if (letters[i].x > (i * 14) && letters[i].xm > -4)
  	  letters[i].xm--;
        else if (letters[i].x < (i * 14) && letters[i].xm < 4)
  	  letters[i].xm++;

        if (letters[i].y > 100 && letters[i].ym > -4)
          letters[i].ym--;
        else if (letters[i].y < 100 && letters[i].ym < 4)
          letters[i].ym++;


        /* Snap into place: */

        if (letters[i].x >= (i * 14) - 8 &&
            letters[i].x <= (i * 14) + 8 &&
  	    letters[i].y >= 92 &&
	    letters[i].y <= 108 &&
	    (letters[i].xm != 0 ||
	     letters[i].ym != 0))
        {
  	  letters[i].x = i * 14;
   	  letters[i].xm = 0;
        
 	  letters[i].y = 100;
          letters[i].ym = 0;

	  snapped++;
        }
      }
    }


    /* Draw screen: */
    
    /* (Title) */
    
    for (i = 0; i < 11; i++)
    {
      draw_char(titlestr[i], letters[i].x + xoff, letters[i].y, 10);
    }
    
    
    /* (Credits) */
    
    if (snapped == 11)
    {
      draw_text("BY BILL KENDRICK", 16 + xoff, 140, 5);
      draw_text("NEW BREED SOFTWARE", 8 + xoff, 155, 5);

      sprint_num(str, "HIGH ", high);
      draw_text(str, 32 + xoff, 5, 5);
      draw_text(str, 33 + xoff, 6, 5);

      if (score != 0 && (score != high || (counter % 20) < 10))
      {
	if (game_pending == 0)
          sprint_num(str, "LAST ", score);
	else
          sprint_num(str, "SCR  ", score);
        draw_text(str, 32 + xoff, 25, 5);
        draw_text(str, 33 + xoff, 26, 5);
      }
    }


    draw_text("START", 55 + xoff, 180, 5);

    if (game_pending)
      draw_text("CONTINUE", 40 + xoff, 200, 5);


    /* (Giant rock) */

    /*
    draw_segment(40 / size, 0, 30 / size, 30, x, y, angle);
    draw_segment(30 / size, 30, 40 / size, 55, x, y, angle);
    draw_segment(40 / size, 55, 25 / size, 90, x, y, angle);
    draw_segment(25 / size, 90, 40 / size, 120, x, y, angle);
    draw_segment(40 / size, 120, 35 / size, 130, x, y, angle);
    draw_segment(35 / size, 130, 40 / size, 160, x, y, angle);
    draw_segment(40 / size, 160, 30 / size, 200, x, y, angle);
    draw_segment(30 / size, 200, 45 / size, 220, x, y, angle);
    draw_segment(45 / size, 220, 25 / size, 265, x, y, angle);
    draw_segment(25 / size, 265, 30 / size, 300, x, y, angle);
    draw_segment(30 / size, 300, 45 / size, 335, x, y, angle);
    draw_segment(45 / size, 335, 40 / size, 0, x, y, angle);
    */


    /* (Agenda logo!) */
    /* (i'm very proud!) */

    draw_segment(35 / size, 90 + 35, 30 / size, 90 + 20, x, y, angle);
    draw_segment(30 / size, 90 + 20, 22 / size, 90 + 27, x, y, angle);
    draw_segment(22 / size, 90 + 27, 26 / size, 90 + 45, x, y, angle);
    draw_segment(26 / size, 90 + 45, 35 / size, 90 + 35, x, y, angle);

    draw_segment(30 / size, 90 + 20, 40 / size, 90 + 16, x, y, angle);
    draw_segment(40 / size, 90 + 16, 38 / size, 84, x, y, angle);
    draw_segment(38 / size, 84, 28 / size, 82, x, y, angle);
    draw_segment(28 / size, 82, 30 / size, 90 + 20, x, y, angle);

    draw_segment(28 / size, 82, 32 / size, 58, x, y, angle);
    draw_segment(32 / size, 58, 22 / size, 37, x, y, angle);
    draw_segment(22 / size, 37, 14 / size, 74, x, y, angle);
    draw_segment(14 / size, 74, 28 / size, 82, x, y, angle);

    draw_segment(22 / size, 37, 35 / size, 24, x, y, angle);
    draw_segment(35 / size, 24, 31 / size, 360 - 7, x, y, angle);
    draw_segment(31 / size, 360 - 7, 19 / size, 360 - 15, x, y, angle);
    draw_segment(19 / size, 360 - 15, 22 / size, 37, x, y, angle);

    draw_segment(19 / size, 360 - 15, 10 / size, 360 - 33, x, y, angle);
    draw_segment(10 / size, 360 - 33, 24 / size, 360 - 68, x, y, angle);
    draw_segment(24 / size, 360 - 68, 30 / size, 360 - 53, x, y, angle);
    draw_segment(30 / size, 360 - 53, 19 / size, 360 - 15, x, y, angle);

    draw_segment(10 / size, 360 - 33, 10 / size, 30, x, y, angle);
    draw_segment(10 / size, 30, 12 / size, 180 - 24, x, y, angle);
    draw_segment(12 / size, 180 - 24, 11 / size, 180 + 20, x, y, angle);
    draw_segment(11 / size, 180 + 20, 10 / size, 360 - 33, x, y, angle);

    draw_segment(11 / size, 180 + 20, 30 / size, 180 + 10, x, y, angle);
    draw_segment(30 / size, 180 + 10, 37 / size, 180 + 39, x, y, angle);
    draw_segment(37 / size, 180 + 39, 26 / size, 180 + 65, x, y, angle);
    draw_segment(26 / size, 180 + 65, 11 / size, 180 + 20, x, y, angle);

    draw_segment(26 / size, 180 + 65, 24 / size, 360 - 68, x, y, angle);
    draw_segment(24 / size, 360 - 68, 38 / size, 360 - 76, x, y, angle);
    draw_segment(38 / size, 360 - 76, 39 / size, 180 + 73, x, y, angle);
    draw_segment(39 / size, 180 + 73, 26 / size, 180 + 65, x, y, angle);

    draw_segment(30 / size, 360 - 53, 36 / size, 360 - 41, x, y, angle);
    draw_segment(36 / size, 360 - 41, 43 / size, 360 - 50, x, y, angle);
    draw_segment(43 / size, 360 - 50, 37 / size, 360 - 61, x, y, angle);
    draw_segment(37 / size, 360 - 61, 30 / size, 360 - 53, x, y, angle);

    draw_segment(36 / size, 360 - 41, 34 / size, 360 - 35, x, y, angle);
    draw_segment(34 / size, 360 - 35, 42 / size, 360 - 28, x, y, angle);
    draw_segment(42 / size, 360 - 28, 44 / size, 360 - 33, x, y, angle);
    draw_segment(44 / size, 360 - 33, 36 / size, 360 - 41, x, y, angle);

  }
  while (!done);

  return(quit);
}



/* --- GAME --- */

int game(void)
{
  int done, quit, counter;
  int i, j;
  int num_asteroids_alive;
  char str[10];
  
  
  done = 0;
  quit = 0;
  counter = 0;
  
  want_sound = 0;
  snd_pitch = 100;
  snd_dur = 0;
  
  left_pressed = 0;
  right_pressed = 0;
  up_pressed = 0;
  shift_pressed = 0;

  if (game_pending == 0)
  {  
    lives = 3;
    score = 0;
  
    player_alive = 1;
    player_die_timer = 0;
    angle = 90;
    x = (WIDTH / 2) << 4;
    y = (HEIGHT / 2) << 4;
    xm = 0;
    ym = 0;

    level = 1;
    reset_level();
  }
 
  game_pending = 1; 
  
  do
    {
      counter++;
      
      
      /* Handle events: */
      newframe();
      get_buttons(&left_pressed, &right_pressed, &up_pressed, &shift_pressed);      
      
      /* Handle player death: */
      
      if (player_alive == 0)
	{
	  player_die_timer--;
	  
	  if (player_die_timer <= 0)
	    {
	      if (lives > 0)
	      {
	        /* Reset player: */
	      
  	        player_die_timer = 0;
	        angle = 90;
	        x = (WIDTH / 2) << 4;
	        y = (HEIGHT / 2) << 4;
	        xm = 0;
	        ym = 0;
	      
	      
	        /* Only bring player back when it's alright to! */
	      
	        player_alive = 1;
	     
	        if (!shift_pressed)
		{	
	          for (i = 0; i < NUM_ASTEROIDS && player_alive; i++)
	    	    {
	 	      if (asteroids[i].alive)
		        {
		          if (asteroids[i].x >= (x >> 4) - 50 &&
			      asteroids[i].x <= (x >> 4) + 50 &&
			      asteroids[i].y >= (y >> 4) - 40 &&
	 		      asteroids[i].y <= (y >> 4) + 40)
			    {
			      /* If any asteroid is too close for comfort,
			         don't bring ship back yet! */
			  
			      player_alive = 0;
			    }
		        }
	 	    }
		}
	      }
	      else
	      {
	        done = 1;
		game_pending = 0;
	      }
	    }
	}
      
      
      /* Move ship: */
      
      x = x + xm;
      y = y + ym;
      
      
      /* Wrap ship around edges of screen: */
      
      if (x >= (WIDTH << 4))
	x = x - (WIDTH << 4);
      else if (x < 0)
	x = x + (WIDTH << 4);
      
      if (y >= (HEIGHT << 4))
	y = y - (HEIGHT << 4);
      else if (y < 0)
	    y = y + (HEIGHT << 4);
      
      
      /* Move bullets: */
      
      for (i = 0; i < NUM_BULLETS; i++)
	{
	  if (bullets[i].timer >= 0)
	    {
	      /* Bullet wears out: */
	      
	      bullets[i].timer--;
	      
	      
	      /* Move bullet: */
	      
	      bullets[i].x = bullets[i].x + bullets[i].xm;
	      bullets[i].y = bullets[i].y + bullets[i].ym;
	      
	      
	      /* Wrap bullet around edges of screen: */
	      
	      if (bullets[i].x >= WIDTH)
		bullets[i].x = bullets[i].x - WIDTH;
	      else if (bullets[i].x < 0)
		bullets[i].x = bullets[i].x + WIDTH;
	      
	      if (bullets[i].y >= HEIGHT)
		bullets[i].y = bullets[i].y - HEIGHT;
	      else if (bullets[i].y < 0)
		bullets[i].y = bullets[i].y + HEIGHT;
	      
	      
	      /* Check for collision with any asteroids! */
	      
	      for (j = 0; j < NUM_ASTEROIDS; j++)
		{
		  if (bullets[i].timer > 0 && asteroids[j].alive)
		    {
		      if ((bullets[i].x + 5 >=
			   asteroids[j].x - asteroids[j].size * AST_RADIUS) &&
			  (bullets[i].x - 5<=
			   asteroids[j].x + asteroids[j].size * AST_RADIUS) &&
			  (bullets[i].y + 5 >=
			   asteroids[j].y - asteroids[j].size * AST_RADIUS) &&
			  (bullets[i].y - 5 <=
			   asteroids[j].y + asteroids[j].size * AST_RADIUS))
			{
			  /* Remove bullet! */
			  
			  bullets[i].timer = 0;
			  
			  
			  hurt_asteroid(j, bullets[i].xm, bullets[i].ym,
					asteroids[j].size * 3);
			}
		    }
		}
	    }
	}
      
      
      /* Move asteroids: */
      
      num_asteroids_alive = 0;
      
      for (i = 0; i < NUM_ASTEROIDS; i++)
	{
	  if (asteroids[i].alive)
	    {
	      num_asteroids_alive++;
	      
	      /* Move asteroid: */
	      
	      if ((counter % 4) == 0)
		{
		  asteroids[i].x = asteroids[i].x + asteroids[i].xm;
		  asteroids[i].y = asteroids[i].y + asteroids[i].ym;
		}
	      
	      
	      /* Wrap asteroid around edges of screen: */
	      
	      if (asteroids[i].x >= WIDTH)
		asteroids[i].x = asteroids[i].x - WIDTH;
	      else if (asteroids[i].x < 0)
		asteroids[i].x = asteroids[i].x + WIDTH;
	      
	      if (asteroids[i].y >= HEIGHT)
		asteroids[i].y = asteroids[i].y - HEIGHT;
	      else if (asteroids[i].y < 0)
		asteroids[i].y = asteroids[i].y + HEIGHT;
	      
	      
	      /* Rotate asteroid: */
	      
	      asteroids[i].angle = (asteroids[i].angle +
				    asteroids[i].angle_m);
	      
	      
	      /* Wrap rotation angle... */
	      
	      if (asteroids[i].angle < 0)
		asteroids[i].angle = asteroids[i].angle + 360;
	      else if (asteroids[i].angle >= 360)
		asteroids[i].angle = asteroids[i].angle - 360;
	      
	      
	      /* See if we collided with the player: */
	      
	      if (asteroids[i].x >= (x >> 4) - 10 &&
		  asteroids[i].x <= (x >> 4) + 10 &&
		  asteroids[i].y >= (y >> 4) - 10 &&
		  asteroids[i].y <= (y >> 4) + 10 &&
		  player_alive)
		{
		  hurt_asteroid(i, xm >> 4, ym >> 4, NUM_BITS);
		  
		  player_alive = 0;
		  player_die_timer = 30;
		  
		  lives--;

		  if (lives == 0)
		  {
	            player_die_timer = 100;
		  }
		}
	    }
	}
      
      
      /* Move bits: */
      
      for (i = 0; i < NUM_BITS; i++)
	{
	  if (bits[i].timer > 0)
	    {
	      /* Countdown bit's lifespan: */
	      
	      bits[i].timer--;
	      
	      
	      /* Move the bit: */
	      
	      bits[i].x = bits[i].x + bits[i].xm;
	      bits[i].y = bits[i].y + bits[i].ym;


	      /* Wrap bit around edges of screen: */
	      
	      if (bits[i].x >= WIDTH)
		bits[i].x = bits[i].x - WIDTH;
	      else if (bits[i].x < 0)
		bits[i].x = bits[i].x + WIDTH;
	      
	      if (bits[i].y >= HEIGHT)
		bits[i].y = bits[i].y - HEIGHT;
	      else if (bits[i].y < 0)
		bits[i].y = bits[i].y + HEIGHT;
	    }
	}


      /* Draw ship: */
      
      if (player_alive)
	{
	  draw_segment(10, 0,
		       5, 135,
		       x >> 4, y >> 4,
		       angle);
	  
	  draw_segment(5, 135,
		       0, 0,
		       x >> 4, y >> 4,
		       angle);
	  
	  draw_segment(0, 0,
		       5, 225,
		       x >> 4, y >> 4,
		       angle);
	  
	  draw_segment(5, 225,
		       10, 0,
		       x >> 4, y >> 4,
		       angle);
	  
	  
	  /* Draw flame: */
	  
	  if (up_pressed)
	    {
	      draw_segment(0, 0,
			   (rand() % 10), 180,
			   x >> 4, y >> 4,
			   angle);
	    }
	}
      
      
      /* Draw bullets: */
      
      for (i = 0; i < NUM_BULLETS; i++)
	{
	  if (bullets[i].timer >= 0)
	    {
	      draw_thick_line(bullets[i].x - (rand() % 5),
			      bullets[i].y - (rand() % 5),
			      bullets[i].x + (rand() % 5),
			      bullets[i].y + (rand() % 5));
	      
	      draw_thick_line(bullets[i].x + (rand() % 5),
			      bullets[i].y - (rand() % 5),
			      bullets[i].x - (rand() % 5),
			      bullets[i].y + (rand() % 5));
	    }
	}
      
      
      /* Draw asteroids: */
      
      for (i = 0; i < NUM_ASTEROIDS; i++)
	{
	  if (asteroids[i].alive)
	    {
	      draw_asteroid(asteroids[i].size,
			    asteroids[i].x, asteroids[i].y,
			    asteroids[i].angle,
			    asteroids[i].shape);
	    }
	}


      /* Draw bits: */
      
      for (i = 0; i < NUM_BITS; i++)
	{
	  if (bits[i].timer > 0)
	    {
	       line_abs(bits[i].x, bits[i].y,
	                bits[i].x + bits[i].xm,
		       	bits[i].y + bits[i].ym);
	    }
	}

      
      /* Draw score: */
      
      sprint_num(str, "", score);
      draw_text(str, 3, 3, 7);
      draw_text(str, 4, 4, 7);


      /* Level: */
      
      sprint_num(str, "", level);
      draw_text(str, WIDTH / 2 - 5, 3, 7);
      draw_text(str, WIDTH / 2 - 4, 4, 7);

      
      /* Draw lives: */

      for (i = 0; i < lives; i++)
	{
	  draw_segment(8, 0,
		       4, 135,
		       WIDTH - 10 - i * 10, 10,
		       90);
	  
	  draw_segment(4, 135,
		       0, 0,
		       WIDTH - 10 - i * 10, 10,
		       90);
	  
	  draw_segment(0, 0,
		       4, 225,
		       WIDTH - 10 - i * 10, 10,
		       90);
	  
	  draw_segment(4, 225,
		       8, 0,
		       WIDTH - 10 - i * 10, 10,
		       90);
	}


      /* Zooming level effect: */

      if (text_zoom > 0)
      {
	text_zoom--;

	draw_text(zoom_str, (WIDTH - (STRLEN(zoom_str) * text_zoom)) / 2,
		            (HEIGHT - text_zoom) / 2,
		  text_zoom);
      }


      /* Game over? */

      if (player_alive == 0 && lives == 0)
      {
	if (player_die_timer > 7)
	{
	  draw_text("GAME OVER",
	            (WIDTH - 9 * player_die_timer) / 2,
	  	    (HEIGHT - player_die_timer) / 2,
		    player_die_timer);
	}
	else
	{
	  draw_text("GAME OVER",
	            48 + (WIDTH - 160) / 2, 116 + (HEIGHT - 240) / 2, 7);
	}
      }

      /* Go to next level? */
      
      if (num_asteroids_alive == 0)
	{
	  level++;
	  
	  reset_level();
	}

      
      /* Play any sounds: */
      
      if (want_sound)
	{
	  want_sound = 0;
	  
	  do_playsound();
	}

    }
  while (!done);


  /* Record, if a high score: */

  if (score >= high)
  {
    high = score;
  }

  return(quit);
}


void finish(void)
{

}


void setup(void)
{
  int screen, black, white;
  char wname[64];


  /* Options: */

  score = 0;
  sound = 1;

#ifdef REAL_KEYBOARD
  buttonmode = 0;
#else
  buttonmode = 1;
#endif

}


/* Fast approximate-integer, table-based cosine! Whee! */

int fast_cos(int angle)
{
  angle = (angle % 45);
  
  if (angle < 12)
    return(trig[angle]);
  else if (angle < 23)
    return(-trig[10 - (angle - 12)]);
  else if (angle < 34)
    return(-trig[angle - 22]);
  else
    return(trig[45 - angle]);
}


/* Sine based on fast cosine... */

int fast_sin(int angle)
{
  return(- fast_cos((angle + 11) % 45));
}


/* Draw a line: */

void draw_line(int x1, int y1, int x2, int y2)
{
  line_abs(x1, y1, x2, y2);
 
  if (x1 < 0 || x2 < 0)
  {
    line_abs(x1 + WIDTH, y1, x2 + WIDTH, y2);
  }
  else if (x1 >= WIDTH || x2 >= WIDTH)
  {
    line_abs(x1 - WIDTH, y1, x2 - WIDTH, y2);
  }

  if (y1 < 0 || y2 < 0)
  {
    line_abs(x1, y1 + HEIGHT, x2, y2 + HEIGHT);
  }
  else if (y1 >= HEIGHT || y2 >= HEIGHT)
  {
    line_abs(x1, y1 - HEIGHT, x2, y2 - HEIGHT);
  }
}


/* Draw a line segment, rotated around a center point: */

void draw_segment(int r1, int a1,
		  int r2, int a2,
		  int cx, int cy, int a)
{
  draw_line(((fast_cos((a1 + a) >> 3) * r1) >> 10) + cx,
	    cy - ((fast_sin((a1 + a) >> 3) * r1) >> 10),
	    ((fast_cos((a2 + a) >> 3) * r2) >> 10) + cx,
	    cy - ((fast_sin((a2 + a) >> 3) * r2) >> 10));
}


/* Add a bullet: */

void add_bullet(int x, int y, int a, int xm, int ym)
{
  int i, found;
  
  found = -1;
  
  for (i = 0; i < NUM_BULLETS && found == -1; i++)
    {
      if (bullets[i].timer <= 0)
	found = i;
    }
  
  if (found != -1)
    {
      bullets[found].timer = 25;
      
      bullets[found].x = x;
      bullets[found].y = y;
      
      bullets[found].xm = ((fast_cos(a >> 3) * 5) >> 10) + (xm >> 4);
      bullets[found].ym = - ((fast_sin(a >> 3) * 5) >> 10) + (ym >> 4);
    }
}


/* Add an asteroid: */

void add_asteroid(int x, int y, int xm, int ym, int size)
{
  int i, found;
  
  
  /* Find a slot: */
  
  found = -1;
  
  for (i = 0; i < NUM_ASTEROIDS && found == -1; i++)
    {
      if (asteroids[i].alive == 0)
	found = i;
    }
  
  
  /* Hack: No asteroids should be stationary! */
  
  while (xm == 0)
    {
      xm = (rand() % 3) - 1;
    }
  
  
  if (found != -1)
    {
      asteroids[found].alive = 1;
      
      asteroids[found].x = x;
      asteroids[found].y = y;
      asteroids[found].xm = xm;
      asteroids[found].ym = ym;
      
      asteroids[found].angle = (rand() % 360);
      asteroids[found].angle_m = (rand() % 6) - 3;
      
      asteroids[found].size = size;
      
      for (i = 0; i < AST_SIDES; i++)
	{
	  asteroids[found].shape[i].radius = (rand() % 3);
	  asteroids[found].shape[i].angle = i * 60 + (rand() % 40);
	}
    }
}


/* Add a bit: */

void add_bit(int x, int y, int xm, int ym)
{
  int i, found;
  
  found = -1;
  
  for (i = 0; i < NUM_BITS && found == -1; i++)
    {
      if (bits[i].timer <= 0)
	found = i;
    }
  
  
  if (found != -1)
    {
      bits[found].timer = 16;
      
      bits[found].x = x;
      bits[found].y = y;
      bits[found].xm = xm;
      bits[found].ym = ym;
    }
}


/* Draw an asteroid: */

void draw_asteroid(int size, int x, int y, int angle, shape_type * shape)
{
  int i;
  
  
  /* Square asteroid: */
  
  /*
  draw_segment((size * AST_RADIUS), 45,
	       (size * AST_RADIUS), 135,
	       x, y,
	       angle);
  
  draw_segment((size * AST_RADIUS), 135,
	       (size * AST_RADIUS), 225,
	       x, y,
	       angle);
  
  draw_segment((size * AST_RADIUS), 225,
	       (size * AST_RADIUS), 315,
	       x, y,
	       angle);
  
  draw_segment((size * AST_RADIUS), 315,
	       (size * AST_RADIUS), 45,
	       x, y,
	       angle);
  */
  
  
  /*
  draw_segment((size * AST_RADIUS), 0,
	       (size * AST_RADIUS) / 3, 20,
	       x, y,
	       angle);

  draw_segment((size * AST_RADIUS) / 3, 20,
	       (size * AST_RADIUS) / 2, 50,
	       x, y,
	       angle);

  draw_segment((size * AST_RADIUS) / 2, 50,
	       (size * AST_RADIUS), 90,
	       x, y,
	       angle);

  draw_segment((size * AST_RADIUS), 90,
	       (size * AST_RADIUS) / 2, 160,
	       x, y,
	       angle);

  draw_segment((size * AST_RADIUS) / 2, 160,
	       (size * AST_RADIUS) / 3, 240,
	       x, y,
	       angle);

  draw_segment((size * AST_RADIUS) / 3, 240,
	       (size * AST_RADIUS), 260,
	       x, y,
	       angle);

  draw_segment((size * AST_RADIUS), 260,
	       (size * AST_RADIUS) / 2, 350,
	       x, y,
	       angle);

  draw_segment((size * AST_RADIUS) / 2, 350,
	       (size * AST_RADIUS), 0,
	       x, y,
	       angle);
  */

  
  for (i = 0; i < AST_SIDES - 1; i++)
    {
      draw_segment((size * (AST_RADIUS - shape[i].radius)),
		   shape[i].angle,
		   (size * (AST_RADIUS - shape[i + 1].radius)),
		   shape[i + 1].angle,
		   x, y,
		   angle);
    }

  draw_segment((size * (AST_RADIUS - shape[AST_SIDES - 1].radius)),
	       shape[AST_SIDES - 1].angle,
	       (size * (AST_RADIUS - shape[0].radius)),
	       shape[0].angle,
	       x, y,
	       angle);
}


/* Queue a sound! */

void playsound(int pitch, int duration)
{
  want_sound = 1;
  snd_pitch = pitch;
  snd_dur = duration;
}


/* Play the sound: */

void do_playsound(void)
{
}


/* Break an asteroid and add an explosion: */

void hurt_asteroid(int j, int xm, int ym, int exp_size)
{
  int k;
  
  add_score(100 / (asteroids[j].size + 1));

  if (asteroids[j].size > 1)
    {
      /* Break the rock into two smaller ones! */
      
      add_asteroid(asteroids[j].x,
		   asteroids[j].y,
		   ((asteroids[j].xm + xm) / 2),
		   (asteroids[j].ym + ym),
		   asteroids[j].size - 1);
      
      add_asteroid(asteroids[j].x,
		   asteroids[j].y,
		   (asteroids[j].xm + xm),
		   ((asteroids[j].ym + ym) / 2),
		   asteroids[j].size - 1);
    }

  
  /* Make the original go away: */
  
  asteroids[j].alive = 0;
  
  
  /* Add explosion: */
  
  playsound(1000 / (asteroids[j].size + 1), 10);
  
  for (k = 0; k < exp_size; k++)
    {
      add_bit((asteroids[j].x -
	       (asteroids[j].size * AST_RADIUS) +
	       (rand() % (AST_RADIUS * 2))),
	      (asteroids[j].y -
	       (asteroids[j].size * AST_RADIUS) +
	       (rand() % (AST_RADIUS * 2))),
	      ((rand() % (asteroids[j].size * 3)) -
	       (asteroids[j].size) +
	       ((xm + asteroids[j].xm) / 3)),
	      ((rand() % (asteroids[j].size * 3)) -
	       (asteroids[j].size) +
	       ((ym + asteroids[j].ym) / 3)));
    }
}


/* Increment score: */

void add_score(int amount)
{
  /* See if they deserve a new life: */

  if (score / ONEUP_SCORE < (score + amount) / ONEUP_SCORE)
  {
    lives++;
    STRCPY(zoom_str, "EXTRA LIFE");
    text_zoom = ZOOM_START;
  }



  /* Add to score: */
  
  score = score + amount;
  
  
  /* Erase old score: */
  
}


/* Draw a character: */

void draw_char(char c, int x, int y, int r)
{
  int i, v;
  
  /* Which vector is this character? */
  
  v = -1;
  if (c >= '0' && c <= '9')
    v = (c - '0');
  else if (c >= 'A' && c <= 'Z')
    v = (c - 'A') + 10;
  
  
  if (v != -1)
    {
      for (i = 0; i < 5; i++)
	{
	  if (char_vectors[v][i][0] != -1)
	    {
	      draw_line(x + (char_vectors[v][i][0] * r),
			y + (char_vectors[v][i][1] * r),
			x + (char_vectors[v][i][2] * r),
			y + (char_vectors[v][i][3] * r));
	    }
	}
    }
}


void draw_text(char * str, int x, int y, int s)
{
  int i;

  for (i = 0; i < STRLEN(str); i++)
    draw_char(str[i], i * (s + 3) + x, y, s);
}


/* Draw the close box at the bottom left of the screen: */

void draw_closebox(void)
{
  draw_line( 0, HEIGHT - 16, 15, HEIGHT - 16);
  draw_line(15, HEIGHT - 16, 15, HEIGHT - 1);
  draw_line(15, HEIGHT - 1,  0, HEIGHT - 1);
  draw_line( 0, HEIGHT - 1,  0, HEIGHT - 16);
  
  draw_line( 0, HEIGHT - 16, 15, HEIGHT - 1);
  draw_line( 0, HEIGHT - 1, 15, HEIGHT - 16);
}


/* Draw button mode toggle: */

void draw_buttontoggle(void)
{
  if (buttonmode == 1)
    draw_text("D", WIDTH / 2 - 3, HEIGHT - 11, 5);
  else if (buttonmode == 0)
    draw_text("A", WIDTH / 2 - 3, HEIGHT - 11, 5);
}


/* Draw the sound toggle switch: */

void draw_soundtoggle(void)
{
  draw_line(WIDTH - 16, HEIGHT - 16, WIDTH - 1, HEIGHT - 16);
  draw_line(WIDTH - 1, HEIGHT - 16, WIDTH - 1, HEIGHT - 1);
  draw_line(WIDTH - 1, HEIGHT - 1, WIDTH - 16, HEIGHT - 1);
  draw_line(WIDTH - 16, HEIGHT - 1, WIDTH - 16, HEIGHT - 16);


  draw_line(WIDTH - 5, HEIGHT - 15, WIDTH - 5, HEIGHT - 2);
  draw_line(WIDTH - 5, HEIGHT - 2, WIDTH - 10, HEIGHT - 5);
  draw_line(WIDTH - 10, HEIGHT - 5, WIDTH - 10, HEIGHT - 10);
  draw_line(WIDTH - 10, HEIGHT - 10, WIDTH - 5, HEIGHT - 15);

  if (sound == 0)
  {
    draw_thick_line(WIDTH - 16, HEIGHT - 1, WIDTH - 1, HEIGHT - 16);
  }
}



void draw_thick_line(int x1, int y1, int x2, int y2)
{
  draw_line(x1, y1, x2, y2);
  draw_line(x1 + 1, y1 + 1, x2 + 1, y2 + 1);
}


void reset_level(void)
{
  int i;
  
  
  for (i = 0; i < NUM_BULLETS; i++)
    bullets[i].timer = 0;
  
  for (i = 0; i < NUM_ASTEROIDS; i++)
    asteroids[i].alive = 0;
  
  for (i = 0; i < NUM_BITS; i++)
    bits[i].timer = 0;
  
  for (i = 0; i < (level + 1) && i < 10; i++)
    {
      add_asteroid(/* x */ (rand() % WIDTH),
		   /* y */ (rand() % 20) + (220 * (rand() % 2)),
		   /* xm */ ((rand() % 5) - 2) * 3,
		   /* ym */ (rand() % 5) - 2,
		   /* size */ (rand() % 3) + 2);
    }


  sprint_num(zoom_str, "LEVEL ", level);

  text_zoom = ZOOM_START;
}
