/* Battle.c : Fight a battle between 2 fleets */

#include "bbc.h"
#include "swis.h"
#include "kernel.h"

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>

#include "basic.h"
#include "rand.h"

/******************************** DEFINITIONS *******************************/

#define TRUE  1
#define FALSE 0
#define NULL  0
#define PI    3.1415926536
#define root2 1.4142135624

#define MAXSHIP 200  /* Maximum total number of ships */

/********************************** MACROS **********************************/

#define SQR(A)    ((A)*(A))
#define min(a,b)  ((a<b)?(a):(b))
#define max(a,b)  ((a>b)?(a):(b))

/******************************** GLOBAL DATA *******************************/

FILE *debug;  /* Debug file */

char id[2][11];  /* Player sub-directories */
char ship[MAXSHIP][15];  /* Names of ships */
int mass[MAXSHIP];  /* Masses of ships */
int act[MAXSHIP];  /* Current orders of ships */
int chr[MAXSHIP*2];  /* Character used for ship */
int own[MAXSHIP*2];  /* Owner of ship */
int con[MAXSHIP*2];  /* Number of control units */
int gen[MAXSHIP*2];  /* Number of generators */
int sto[MAXSHIP*2];  /* Number of storage units */
int thr[MAXSHIP*2];  /* Number of thrusters */
int expl[MAXSHIP*2];  /* Number of explosives */
int las[MAXSHIP*2];  /* Number of lasers */
int shi[MAXSHIP*2];  /* Number of shields */
int arm[MAXSHIP*2];  /* Amount of armour */
int dec[MAXSHIP*2];  /* Number of decoys */
int doc[MAXSHIP*2];  /* Number of docks */
int ndoc[MAXSHIP*2];  /* Number of ship in docks */
double x[MAXSHIP];  /* x position of ship */
double y[MAXSHIP];  /* y position of ship */
double u[MAXSHIP];  /* x co-eff of velocity */
double v[MAXSHIP];  /* y ditto */
double estor[MAXSHIP];  /* Energy stored in ship */
double datx[MAXSHIP];  /*  */
double daty[MAXSHIP];  /*  */
int tg[MAXSHIP];  /* Ship targetted at */

/* Variables for pointers & menus */
int xcur1[2], ycur1[2];  /* Position of 1st cursors */
int xcur2[2], ycur2[2];  /* Position of 2nd cursors */
int cur1[2];  /* What 1st cursor to use */
int cur2[2];  /* What 2nd cursor to use */
int cur2on[2];  /* Whether to show 2nd cursor */
int press[2];  /* Button status */
int sh1[2];  /* Ship currently selected */
int mode[2];  /* Current menu mode, determines effect of next click */
int op[2];  /* Option selected on menu */
char opt[2][16][10];  /* Options in order menu */
int mop[2];  /* Number of options in order menu - 1 */
int next[2];  /* What order ship sh1[] will be given at next click */
int mask[2];  /* Determines what player's ships to target, etc. */
int mouse[2];  /* Method of control */
int ptrx[2], ptry[2];  /* co-ords of mouse pointer */

int nship;  /* Total number of ships at start of battle */
int nsh0;  /* Number of player 0 ships at start of battle */
double tick;  /* Time step */
int i;  /* Ship currently being processed */

/* Factors */
#define g 10000  /* Gravitational constant */
#define rstor 5  /* Max energy in 1 storage unit */
#define tfac 0.02  /* Energy required to change vel of 1 mass by 1 */
#define lasrngsq 9216  /* 96^2 = range squared of 1 laser projector */
#define damlas 2  /* Laser damage factor */
#define eshield 2  /* Energy required to recharge 1 shield */
#define rex1sq 1024  /* 32^2 = Radius squared of mass 1 explosion */
#define damex 8  /* Explosion damage factor */

#define decsp 16  /* Max decoy launch speed */
#define dockdsq 16
#define dockvsq 16  /* Used to determine whether dock successful */

const int radius = 48;  /* Radius of planet */
const int radsq = 2304;


/* Character numbers */
#define square 224
#define point  225
#define cross  226
#define explos 227
#define decoy  228

/***************************** GAME ROUTINES ********************************/

char *sp_strncpy(char str[512],int len)
{
  int i;
  char tstr[512];
  char ttstr[512];
  int c;

  strcpy(tstr,"");
  for(i=0 ; i<len ; i++)
  {
    c=str[i];
    sprintf(ttstr,"%s%c",tstr,c);
    strcpy(tstr,ttstr);
  }
  return(tstr);
}

void wait(int delay) {
  int t;

  t = bbc_time();
  while (bbc_time() - t < delay);
}

void player_lost(int player) {

  bbc_vdu(4);
  bbc_vduq(28, 0, 31, 39, 0); /* Text window */
  bbc_colour(2-player);
  bbc_colour(128);
  bbc_cls();
  if (player == 1) {
    printf("\n\n\n   Defender wins!");
  } else {
    printf("\n\n\n   Attacker wins!");
  }
  wait(150);
  exit(0);
}

int nr_targ(int i) {
  /* Return number of nearest hostile ship to ship i */
  int n, near;
  double neardsq, dsq;

  near = -1;
  neardsq = 100000000.0;
  for (n=0; n<nship; n++) {
    if (act[n] >= 0) {
      if (own[i] ^ own[n]) {
        dsq = SQR(x[i] - x[n]) + SQR(y[i] - y[n]);
        if (neardsq > dsq) {
          neardsq = dsq;
          near = n;
        }
      }
    }
  }
  return near;
}

void initp2(void) {
  if (expl[nship] > 0) {
    act[nship] = 9;
    tg[nship] = randint(nsh0);
  }
  if (las[nship] > 0) {
    act[nship] = 8;
    tg[nship] = nr_targ(nship);
  }
}

void read_force_files(void) {
  /* Read in players' forces */
  int ndecoy, pn, dock, row, bits, err;
  int chrmax, chrdef, chrnum, chr0 = 229;
  FILE *file;
  char path[20], read[15];

  /*-fprintf(debug, "\nEntered read_force_files()\n");-*/

  nship = -1;
  nsh0 = 0;
  ndecoy = 0;
  for (pn=0; pn<2; pn++) {
    /*-fprintf(debug, "Started player %d\n", pn);-*/
    chrmax = 0;
    sprintf(path, "<Space$Dir>.Force%1d", pn+1);
    if (file = fopen(path, "rb"), file == NULL) {
      printf("Can't load force for player %d", pn);
      bbc_vdu(7);
      exit(1);
    }
    /*-fprintf(debug, "File %s opened\n", path);-*/
    /* Read player id */
    if (err = fscanf(file, "%s", id[pn]), err == EOF) {
      fprintf(debug, "Error reading player id from file %s\n", path);
      fclose(file);
      exit(1);
    }
    if (err = fgetc(file) /* Discard '\n' */ , err == EOF) {
      fprintf(debug, "Error reading '\n' from file %s\n", path);
      fclose(file);
      exit(1);
    }
    /* Loop for reading ships */
    if (err = fscanf(file, "%13c", read) /* Ship name 1 */ , err == EOF) {
      fprintf(debug, "Error reading ship name 1 from file %s\n", path);
      fclose(file);
      exit(1);
    }
    do {
      nship++;
      own[nship] = pn;
      strcpy(ship[nship], read);
      /* Ship character */
      if (err = fscanf(file, "%3d", &chrnum), err == EOF) {
        fprintf(debug, "Error reading chr[%d] from file %s\n", nship, path);
        fclose(file);
        exit(1);
      }
      chr[nship] = chr0+chrnum;
      chrmax = max(chrmax, chr[nship]);
      /*-fprintf(debug, "Name[%d]=%s, Chr[%d]=%d(%c)\n", nship, ship[nship], nship, chr[nship], chr[nship]);-*/
      /* Ship attributes */
      if (err = fscanf(file, "%2x%2x%2x%2x%2x", &con[nship], &gen[nship], &thr[nship], &sto[nship], &las[nship]), err == EOF) {
        fprintf(debug, "Error reading 1st stats[%d] from file %s\n", nship, path);
        fclose(file);
        exit(1);
      }
      if (err = fscanf(file, "%2x%2x%2x%2x%2x", &shi[nship], &arm[nship], &dec[nship], &expl[nship], &doc[nship]), err == EOF) {
        fprintf(debug, "Error reading 2nd stats[%d] from file %s\n", nship, path);
        fclose(file);
        exit(1);
      }
      /*-fprintf(debug, "con=%d, gen=%d, thr=%d, sto=%d, las=%d\n", con[nship], gen[nship], thr[nship], sto[nship], las[nship]);-*/
      /*-fprintf(debug, "shi=%d, arm=%d, dec=%d, exp=%d, doc=%d\n", shi[nship], arm[nship], dec[nship], expl[nship], doc[nship]);-*/
      /*-fprintf(debug, "doc=%d\n", doc[nship]);-*/
      ndoc[nship] = 0;
      ndecoy = ndecoy + dec[nship];
      /* Read mass, and whether ship is docked */
      if (err = fscanf(file, "%3d%3d", &mass[nship], &act[nship]), err == EOF) {
        fprintf(debug, "Error reading mass[%d] from file %s\n", nship, path);
        fclose(file);
        exit(1);
      }
      /*-fprintf(debug, "mass=%d, act=%d", mass[nship], act[nship]);-*/
      /* Correction to docking numbers */
      if (act[nship] == 0) dock = nship;
      if (act[nship] < 0) {
        act[nship] -= nsh0;
        dock = -act[nship];
        ndoc[dock]++;
      }
      /*-fprintf(debug, ", real act=%d\n", act[nship]);-*/
      /* Temporary control on launch */
      if (act[nship] < 0 && con[nship] == 0) con[nship] = 255;
      /* Initial target search */
      tg[nship] = 1;
      /* Initial energy */
      estor[nship] = rstor * (double)sto[nship];
      if (pn == 1 && act[nship] == 0 && con[nship] == 0) initp2();
      if (err = fscanf(file, "%13c", read), err == EOF) {
        fprintf(debug, "Error reading name[%d] from file %s\n", nship+1, path);
        fclose(file);
        exit(1);
      }

    } while (strcmp(read, "*END*       ") != 0);
    /* Character definitions */
    for (chrdef=chr0; chrdef<=chrmax; chrdef++) {
      bbc_vdu(23); bbc_vdu(chrdef);
      for (row=0; row<8; row++) {
        if (err = fscanf(file, "%03d", &bits), err == EOF) {
          fprintf(debug, "Error reading character defs (row=%d) from file %s\n", row, path);
          fclose(file);
          exit(1);
        }
        bbc_vdu(bits);
      }
    }
    fclose(file);
    /* Increment 0 vars for next player */
    chr0 = chr[nship] + 1;
    if (pn == 0) nsh0 = nship;
  }
  /* Create decoys */
  if (ndecoy > 0) {
    for (pn=0; pn<ndecoy; pn++) {
      nship++;
      strcpy(ship[nship], "DECOY      ");
      chr[nship] = decoy;
      con[nship]=0;
      gen[nship]=0;
      sto[nship]=0;
      thr[nship]=0;
      las[nship]=0;
      expl[nship]=0;
      shi[nship]=0;
      arm[nship]=1;
      dec[nship]=0;
      doc[nship]=0;
      ndoc[nship]=0;
      mass[nship]=1;
      act[nship]=-2000;
      tg[nship]=1;
      estor[nship]=0;
    }
  }
  /* Correct value of nship */
  nship++;
}

void fire_decoy(int t) {
  /* Launch decoy from ship t */
  int d, s;

  if (dec[t] == 0) return;
  dec[t]--;
  /* Create */
  d = 0;
  do d++; while (act[d] != -2000);
  act[d] = 0;
  own[d] = own[t];
  x[d] = x[t];
  y[d] = y[t];
  u[d] = u[t] + rand_int(2 * decsp) - decsp;
  v[d] = v[t] + rand_int(2 * decsp) - decsp;
  /* Show */
  gcol(3, 1+own[d]);
  moveD(x[d], y[d]);
  bbc_vdu(5); bbc_vdu(decoy);
  /* Decoy other ships */
  for (s=0; s<nship; s++) {
    if (tg[s] == t) tg[s] = d;
  }
}

void dock(int t, int s) {
  /* Ship s docks with ship t */
  if (ndoc[t] >= doc[t]) { act[s] = 0; return; }
  act[s] = -t;
  ndoc[t]++;
  mass[t] += mass[s];
  /* If cursor attached, detach it */
  if (sh1[own[s]] != s) return;
  if (cur2on[own[s]]) {
    gcol(3, 1+own[s]);
    move(xcur2[own[s]], ycur2[own[s]]);
    bbc_vdu(5); bbc_vdu(cur2[own[s]]);
    cur2on[own[s]] = FALSE;
  }
  mode[own[s]] = 0;
  bbc_vdu(4);
  bbc_vduq(28, 32, 31-16*own[s], 39, 16-16*own[s]);
  bbc_colour(128);
  bbc_cls();
}

void launch(int s, int t) {
  /* Ship s launches from docking with ship t */
  act[s] = 0;
  x[s] = x[t];
  y[s] = y[t];
  u[s] = u[t];
  v[s] = v[t];
  ndoc[t]--;
  mass[t] -= mass[s];
  gcol(3, 1+own[t]);
  moveD(x[s], y[s]);
  bbc_vdu(5); bbc_vdu(chr[s]);
}

void check_docks(int t) {
  /* Release any docked ships? */
  int nd, s;

  if (ndoc[t] <= doc[t]) return;
  nd = 0;
  for (s=0; s<nship; s++) {
    if (act[s] == -t) {
      nd++;
      if (nd > doc[t]) launch(s, t);
    }
  }
}

void damage(int dam, int t) {
  /* Ship no. t receives dam damages */
  int n, tot, acc, hit;
  if (dam <= 0) return;
  for (n=0; n<dam; n++) {
    /* Take from shields if possible */
    if (shi[t] > 0) {
      shi[t]--;
      goto next;
    }
    /* Take from armour if possible */
    if (arm[t] > 0) {
      arm[t]--;
      goto next;
    }
    /* Count total attributes */
    if (con[t] == 255) con[t] = 0;
    tot = con[t] + gen[t] + sto[t] + thr[t] + las[t] + dec[t] + doc[t];
    if (tot == 0) {
      act[t] = -1000; /* Dead */
      return;
    }
    /* Choose one at random */
    hit = randint(tot);
    /* Remove one */
    if (hit <= con[t]) {con[t]--; goto next;}
    acc = con[t] + gen[t];
    if (hit <= acc) {gen[t]--; goto next;}
    acc += sto[t];
    if (hit <= acc) {sto[t]--; goto next;}
    acc += thr[t];
    if (hit <= acc) {thr[t]--; goto next;}
    acc += las[t];
    if (hit <= acc) {las[t]--; goto next;}
    acc += dec[t];
    if (hit <= acc) {dec[t]--; goto next;}
    acc += doc[t];
    if (hit <= acc) {doc[t]--; check_docks(t);}

    next:
    ;
  }
  /* Decoy? */
  if (dec[t] > 0) fire_decoy(t);
}

void burst(int x, int y, int rsq) {
  int rsqstep, rsq1, r2, r;
  rsqstep = rsq / 8;
  x += 16;
  y += 16;
  for (rsq1=rsqstep; rsq1<=rsq; rsq1+=rsqstep) {
    r = (int)sqrt((double)rsq1);
    r2 = r / (int)root2;
    bbc_plot(69, x+r, y);
    bbc_plot(69, x+r2, y+r2);
    bbc_plot(69, x, y+r);
    bbc_plot(69, x-r2, y+r2);
    bbc_plot(69, x-r, y);
    bbc_plot(69, x-r2, y-r2);
    bbc_plot(69, x, y-r);
    bbc_plot(69, x+r2, y-r2);
    wait(0);
  }
}

void explode(int s) {
  /* Ship s explodes */
  double rexsq, dsq;
  int t, wait;

  /* Erase ship */
  bbc_gcol(3, 1+own[s]);
  moveD(x[s], y[s]);
  bbc_vdu(5); bbc_vdu(chr[s]);
  /* Show explosion */
  bbc_gcol(3, 3);
  moveD(x[s], y[s]);
  bbc_vdu(5); bbc_vdu(explos);
  wait = bbc_time() + 50;
  /* Damage other ships? */
  if (expl[s] > 0) {
    /* Compute radius squared */
    rexsq = (double)expl[s] * rex1sq;
    /* Show burst */
    bbc_gcol(3, 3);
    burst((int)x[s], (int)y[s], (int)rexsq);
    /* Test for other ships within burst */
    for (t=nship-1; t>=0; t--) {
      if (act[t] >= 0) {
        dsq = SQR(x[s]-x[t]) + SQR(y[s]-y[t]);
        if (dsq < rexsq)
          damage((int)((damex * sqrt(expl[s])) * (rexsq-dsq) / rexsq), t);
      }
    }
    /* Erase burst */
    gcol(3, 3);
    burst((int)x[s], (int)y[s], (int)rexsq);
  }
  while (wait > bbc_time());
  /* Erase explosion */
  gcol(3, 3);
  moveD(x[s], y[s]);
  bbc_vdu(5); bbc_vdu(explos);
  /* Show explosion complete */
  act[s] = -1001;
  /* If cursor attached, detach it */
  if (sh1[own[s]] != s) return;
  if (cur2on[own[s]]) {
    gcol(3, 1+own[s]);
    move(xcur2[own[s]], ycur2[own[s]]);
    bbc_vdu(5); bbc_vdu(cur2[own[s]]);
    cur2on[own[s]] = FALSE;
  }
  mode[own[s]] = 0;
  bbc_vdu(4);
  bbc_vduq(28, 32, 31-16*own[s], 39, 16-16*own[s]);
  bbc_colour(128);
  bbc_cls();
}

void laser(int i, int targ) {
  /* Ship i attempts to fire at ship targ */
  double rngsq;
  int n;

  /* Parameter check (from nr_targ()) */
  if (targ == -1) return;
  if (act[targ] < 0) { act[i] = 0; return; }
  /* Range */
  rngsq = SQR(x[i] - x[targ]) + SQR(y[i] - y[targ]);
  if (rngsq > lasrngsq * sqrt((double)las[i])) return;
  /* Energy cost */
  if (estor[i] < las[i]) return;
  estor[i] -= las[i];
  /* Draw rays */
  gcol(3, 1+own[i]);
  for (n=0; n<6; n++) {
    moveD(x[i] + 16.0, y[i] - 16.0);
    draw((int)(x[targ] + 16.0), (int)(y[targ] - 16.0));
    wait(1);
  }
  /* Effect */
  damage((int)(damlas * sqrt((double)las[i]) * (lasrngsq * sqrt((double)las[i]) - rngsq) / (lasrngsq * sqrt((double)las[i]))), targ);

}

void try_laser(void) {
  /* See if an enemy ship is in range */
  double rngsq;
  if (own[i] != own[tg[i]] && act[tg[i]] >= 0) {
    rngsq = SQR(x[i] - x[tg[i]]) + SQR(y[i] - y[tg[i]]);
    if (rngsq < lasrngsq * sqrt((double)las[i])) {
      laser(i, tg[i]);
      return;
    }
  }
  tg[i]++;
  if (tg[i] >= nship) tg[i] = 0;
}

void req_vel(double *du, double *dv) {
  /* Return the thrust which ship i requires in du & dv */
  double xdiff, ydiff, diff, acc, fac, or, ov, u1, v1;

  if (act[i] == 0 || act[i] == 10) {
    *du = 0;
    *dv = 0;
    return;
  }
  if (act[i] == 1) {
    /* Stop */
    *du = -u[i];
    *dv = -v[i];
    return;
  }
  if (act[i] == 2) {
    /* Move to position */
    xdiff = datx[i] - x[i];
    ydiff = daty[i] - y[i];
    diff = sqrt(xdiff*xdiff + ydiff*ydiff);
    acc = min(thr[i], gen[i]) / (tfac * mass[i]);
    if (diff == 0) fac = 0; else fac = sqrt(2 * acc / diff);
    *du = xdiff * fac - u[i];
    *dv = ydiff * fac - v[i];
    return;
  }
  if (act[i] == 3) {
    /* Orbit */
    or = sqrt(SQR(x[i]) + SQR(y[i]));
    ov = sqrt(g / or);
    u1 = ov * y[i] / or;
    v1 = -ov * x[i] / or;
    if (u1 * u[i] + v1 * v[i] < 0) {
      u1 = -u1;
      v1 = -v1;
    }
    *du = u1 - u[i];
    *dv = v1 - v[i];
    if (fabs(*du) + fabs(*dv) < 0.1) act[i] = 0;
    return;
  }
  if (act[i] == 8 || act[i] == 9) {
    /* Choose new target? */
    if (act[tg[i]] < 0) {
      tg[i] = nr_targ(i);
      if (tg[i] == -1) {
        act[i] = 0;
        *du = 0;
        *dv = 0;
        return;
      }
    }
  }
  if (act[i] == 8 || act[i] == 9 || act[i] == 7 || act[i] == 4) {
    /* Approach another ship */
    if (act[tg[i]] < 0) {
      act[i] = 0;
      *du = 0;
      *dv = 0;
      return;
    }
    xdiff = x[tg[i]] - x[i];
    ydiff = y[tg[i]] - y[i];
    diff = sqrt(xdiff*xdiff + ydiff*ydiff);
    acc = min(thr[i], gen[i]) / (tfac * mass[i]);
    if (diff == 0) fac = 0; else fac = sqrt(2 * acc / diff);
    *du = xdiff * fac - u[i] + u[tg[i]];
    *dv = ydiff * fac - v[i] + v[tg[i]];
    return;
  }
  if (act[i] == 5) {
    /* Match velocity with another ship */
    if (act[tg[i]] < 0) {
      act[i] = 0;
      *du = 0;
      *dv = 0;
      return;
    }
    *du = u[tg[i]] - u[i];
    *dv = v[tg[i]] - v[i];
    return;
  }
  if (act[i] == 6) {
    /* Avoid another ship; accelerate normal to other ship's velocity */
    if (act[tg[i]] < 0) {
      act[i] = 0;
      *du = 0;
      *dv = 0;
      return;
    }
    *du = 100000.0 * v[tg[i]];
    *du = -100000.0 * u[tg[i]];
    if ((*du) * (u[tg[i]]-u[i]) + (*dv) * (v[tg[i]]-v[i]) > 0) {
      *du = -(*du);
      *dv = -(*dv);
    }
    return;
  }
  /* Aaargh! Unknown order! */
  bbc_vdu(7); bbc_vdu(7);
  bbc_colour(128); bbc_colour(3); bbc_cls();
  bbc_tab(0, 0); printf("Unknown order code '%d'.", act[i]);
  exit(1);
}

void do_ship(void) {
  /* Move ship shp */
  double fac, rsq, du, dv, ereq, poss, ratio;
  int j, s, res;
  /* Used for improved matching: */
  int jmatch = FALSE, tgt;
  double tgx, tgy;

  /* Data offset for original attributes: */
  j = i + nship;
  /* Explode? */
  if (act[i] == 9) {
    if (SQR(x[i] - x[tg[i]]) + SQR(y[i] - y[tg[i]]) < rex1sq/2)
      act[i] = -1000;
  }
  if (act[i] == -1000) {
    explode(i);
    return;
  }
  if (act[i] == -1001) return;
  /* Added by JC: Increase power of match command */
  if (act[i] == 5 && act[tg[i]] >= 0) {
    jmatch = TRUE;
    tgt = tg[i];
    tgx = datx[i];
    tgy = daty[i];
    tg[i] = tg[tgt];
    act[i] = act[tgt];
    datx[i] = datx[tgt];
    daty[i] = daty[tgt];
  }
  /* Energy generation */
  estor[i] += (tick * gen[i]);
  /* Replenish shields? */
  if (shi[i] < shi[j]) {
    res = min(shi[j] - shi[i], (int)(estor[i] / eshield));
    shi[i] += res;
    estor[i] -= ((double)res * eshield);
  }
  /* Docked? or decoy */
  if (act[i] < 0) return;
  /* Laser attack? */
  if (act[i] == 8 && act[tg[i]] < 0) {
    tg[i] = nr_targ(i);
    if (tg[i] == -1) act[i] = 0;
  }
  if (act[i] == 8 || act[i] == 10) {
    laser(i, tg[i]);
  }
  /* Missile (hit)? */
  if (act[i] == 9 && act[tg[i]] < 0) {
    tg[i] = nr_targ(i);
    if (tg[i] == -1) act[i] = 0;
  }
  /* Gravity */
  rsq = SQR(x[i]) + SQR(y[i]);
  if (rsq < radsq) {
    act[i] = -1000;
    explode(i);
    return;
  }
  fac = 1.0 / pow(rsq, 1.5);
  u[i] = u[i] - tick * g * x[i] * fac;
  v[i] = v[i] - tick * g * y[i] * fac;
  /* Determine required thrust */
  req_vel(&du, &dv);
  /* Enough energy? */
  ereq = sqrt(du*du + dv*dv) * tick * tfac * mass[i];
  poss = tick * min(thr[i], estor[i]);
  if (ereq > poss) {
    ratio = poss / ereq;
    du *= ratio;
    dv *= ratio;
    ereq = poss;
  }
  /* Apply thrust */
  u[i] += tick*du;
  v[i] += tick*dv;
  /* Erase prev */
  bbc_gcol(3, 1+own[i]);
  moveD(x[i], y[i]);
  bbc_vdu(5); bbc_vdu(chr[i]);
  /* Move */
  x[i] += tick*u[i];
  y[i] += tick*v[i];
  /* Dock? */
  if (act[i] == 7) {
    if (SQR(x[i]-x[tg[i]]) + SQR(y[i]-y[tg[i]]) < dockdsq) {
      if (SQR(u[i]-u[tg[i]]) + SQR(v[i] - v[tg[i]]) < dockvsq)
        dock(i, tg[i]);
    }
  }
  if (act[i] >= 0) {
    /* Plot in new position */
    gcol(3, 1+own[i]);
    moveD(x[i], y[i]);
    bbc_vdu(5); bbc_vdu(chr[i]);
    /* Use remaining energy for laser? */
    if (act[i] >= 0 && act[i] <= 3) {
      if (las[i] > 0 && estor[i] >= las[i]) {
        try_laser();
      }
    }
  }
  /* Energy storage limit */
  estor[i] = min(estor[i], rstor*(double)sto[i]);
  /* Return to match status */
  if (jmatch) {
    act[i] = 5;
    tg[i] = tgt;
    datx[i] = tgx;
    daty[i] = tgy;
  }
  /* Show ship information? */
  if (sh1[own[i]] != i) return;
  if (mode[own[i]] != 1) return;
  /* Remove secondary cursor? */
  if (cur2on[own[i]]) {
    gcol(3, 1+own[i]);
    move(xcur2[own[i]], ycur2[own[i]]);
    bbc_vdu(5); bbc_vdu(cur2[own[i]]);
    cur2on[own[i]] = FALSE;
  }
  bbc_vdu(4);
  bbc_vduq(28, 32, 31-16*own[i], 39, 16-16*own[i]); /* Text window */
  bbc_colour(0); bbc_colour(129+own[i]);
  bbc_tab(0, 0);
  if (con[i] == 0) {
    bbc_cls();
    printf("%.9s\n\nNO\nRESPONSE", ship[i]);
    return;
  }
  printf("%.9s\n", ship[i]);
  if (con[j] != 255) printf("CON%3d%2d", con[i], con[j]);
  if (gen[j]) printf("GEN%3d%2d", gen[i], gen[j]);
  if (sto[j]) printf("STO%3d%2d", sto[i], sto[j]);
  if (thr[j]) printf("THR%3d%2d", thr[i], thr[j]);
  if (las[j]) printf("LAS%3d%2d", las[i], las[j]);
  if (expl[j]) printf("EXP%3d%2d", expl[i], expl[j]);
  if (shi[j]) printf("SHI%3d%2d", shi[i], shi[j]);
  if (arm[j]) printf("ARM%3d%2d", arm[i], arm[j]);
  if (dec[j]) printf("DEC%3d%2d", dec[i], dec[j]);
  if (doc[j]) printf("DOC%3d%2d", doc[i], doc[j]);
  /*-fprintf(debug, "Displaying: doc[i]=%d, doc[j]=%d\n", doc[i], doc[j]);-*/
  if (sto[j]) printf("E:%4.1f ", estor[i]);
  bbc_colour(0); bbc_colour(129+own[i]);
  printf("                        "); /* 24 spaces */
  bbc_vdu(11); bbc_vdu(11); bbc_vdu(11); /* Up 3 lines */
  printf(" ");
  if (doc[i] > 0) {
    bbc_colour(1+own[i]); bbc_colour(128);
    s = -1;
    for (res=0; res<doc[i]; res++) {
      if (s < nship) {
        do s++; while (act[s] != -i && s < nship);
        if (s < nship) bbc_vdu(chr[s]); else bbc_vdu(32);
      } else bbc_vdu(32);
    }
    bbc_colour(0); bbc_colour(129+own[i]);
  }
  printf("\n");
  switch (act[i]) {
    case 0:
      printf("DRIFTING");
      break;
    case 1:
      printf("STOPPING");
      break;
    case 2:
      printf("MOVING  TO %c", cross);
      xcur2[own[i]] = (int) datx[i];
      ycur2[own[i]] = (int) daty[i];
      cur2[own[i]] = cross;
      bbc_move(xcur2[own[i]], ycur2[own[i]]);
      bbc_vdu(5); bbc_vdu(cross);
      cur2on[own[i]] = TRUE;
      break;
    case 3:
      printf("ENTERINGORBIT");
      break;
    case 4:
      printf("APPROACH-ING %c", cross);
      xcur2[own[i]] = (int) x[tg[i]];
      ycur2[own[i]] = (int) y[tg[i]];
      cur2[own[i]] = cross;
      bbc_move(xcur2[own[i]], ycur2[own[i]]);
      bbc_vdu(5); bbc_vdu(cross);
      cur2on[own[i]] = TRUE;
      break;
    case 5:
      printf("MATCHING %c", cross);
      xcur2[own[i]] = (int) x[tg[i]];
      ycur2[own[i]] = (int) y[tg[i]];
      cur2[own[i]] = cross;
      bbc_move(xcur2[own[i]], ycur2[own[i]]);
      bbc_vdu(5); bbc_vdu(cross);
      cur2on[own[i]] = TRUE;
      break;
    case 6:
      printf("AVOIDING %c", cross);
      xcur2[own[i]] = (int) x[tg[i]];
      ycur2[own[i]] = (int) y[tg[i]];
      cur2[own[i]] = cross;
      bbc_move(xcur2[own[i]], ycur2[own[i]]);
      bbc_vdu(5); bbc_vdu(cross);
      cur2on[own[i]] = TRUE;
      break;
    case 7:
      printf("DOCKING WITH %c", cross);
      xcur2[own[i]] = (int) x[tg[i]];
      ycur2[own[i]] = (int) y[tg[i]];
      cur2[own[i]] = cross;
      bbc_move(xcur2[own[i]], ycur2[own[i]]);
      bbc_vdu(5); bbc_vdu(cross);
      cur2on[own[i]] = TRUE;
      break;
    case 8:
      printf("ATTACK  -ING %c", cross);
      xcur2[own[i]] = (int) x[tg[i]];
      ycur2[own[i]] = (int) y[tg[i]];
      cur2[own[i]] = cross;
      bbc_move(xcur2[own[i]], ycur2[own[i]]);
      bbc_vdu(5); bbc_vdu(cross);
      cur2on[own[i]] = TRUE;
      break;
    case 9:
      printf("AIMED AT%c", cross);
      xcur2[own[i]] = (int) x[tg[i]];
      ycur2[own[i]] = (int) y[tg[i]];
      cur2[own[i]] = cross;
      bbc_move(xcur2[own[i]], ycur2[own[i]]);
      bbc_vdu(5); bbc_vdu(cross);
      cur2on[own[i]] = TRUE;
      break;
    case 10:
      printf("FIRING  AT %c", cross);
      xcur2[own[i]] = (int) x[tg[i]];
      ycur2[own[i]] = (int) y[tg[i]];
      cur2[own[i]] = cross;
      bbc_move(xcur2[own[i]], ycur2[own[i]]);
      bbc_vdu(5); bbc_vdu(cross);
      cur2on[own[i]] = TRUE;
      break;
    default:
      printf("UNKNOWN");
      break;
  }
}

void read_cursor_position(int pn) {
  _kernel_swi_regs r;
  /* Place pointer co-ords into xcur1[pn], ycur1[pn] */
  switch (mouse[pn]) {
    case 1:
      /* JPC's module (keypad) */
      _kernel_swi(0xc0001, &r, &r);
      xcur1[pn] = r.r[0];
      ycur1[pn] = r.r[1];
      break;
    case 2:
      /* JPC's module (left keyboard) */
      break;
    case 3:
    case 4:
      /* Normal mouse */
      _kernel_swi(OS_Mouse, &r, &r);
      xcur1[pn] = r.r[0];
      ycur1[pn] = r.r[1];
      break;
  }
}

int but(int pn) {
  /* Get button state */
  int p = 0;
  _kernel_swi_regs r;

  switch (mouse[pn]) {
    case 3:
      /* Normal mouse, select */
      _kernel_swi(0x1c, &r, &r);
      ptrx[pn] = r.r[0];
      ptry[pn] = r.r[1];
      if (r.r[2] & 4) p = TRUE; else p = FALSE;
      break;
    case 4:
      /* Normal mouse, adjust */
      _kernel_swi(0x1c, &r, &r);
      ptrx[pn] = r.r[0];
      ptry[pn] = r.r[1];
      if (r.r[2] & 1) p = TRUE; else p = FALSE;
      break;
  }
  /*-fprintf(debug, "x=%d, y=%d, p=%d, press=%d, r[2]=%d\n", ptrx[pn], ptry[pn], p, press[pn], r.r[2]);-*/
  if (p & !press[pn]) {
    press[pn] = TRUE;
    return TRUE;
  } else {
    press[pn] = p;
    return FALSE;
  }
  /* press[pn] = p;
  return p; */
}

int nr_ship(int maskpn1, int not, int pn) {
  /* Return number of nearest friendly ship to ptrx[pn],ptry[pn] */
  int n, near;
  double neardsq, dsq;

  near = -1;
  neardsq = 100000000.0;
  for (n=0; n<nship; n++) {
    if (n != not) {
      if ( ((own[n]+1) & maskpn1) && act[n] >= 0) {
        dsq = SQR(ptrx[pn] - x[n]) + SQR(ptry[pn] - y[n]);
        if (neardsq > dsq) {
          neardsq = dsq;
          near = n;
        }
      }
    }
  }
  return near;
}

void create_menu(int pn) {
  /* Store available options in opt[pn][] */

  strcpy(opt[pn][0], "STATUS ");
  strcpy(opt[pn][1], "DRIFT  ");
  strcpy(opt[pn][2], "MATCH  ");
  mop[pn] = 2;
  if (thr[sh1[pn]] > 0) {
    mop[pn]++;
    strcpy(opt[pn][mop[pn]], "STOP   ");
    mop[pn]++;
    strcpy(opt[pn][mop[pn]], "MOVE TO");
    mop[pn]++;
    strcpy(opt[pn][mop[pn]], "ORBIT  ");
    mop[pn]++;
    strcpy(opt[pn][mop[pn]], "APPROCH");
    mop[pn]++;
    strcpy(opt[pn][mop[pn]], "AVOID  ");
    if (doc[sh1[pn]] > ndoc[sh1[pn]]) {
      mop[pn]++;
      strcpy(opt[pn][mop[pn]], "DOCK   ");
    }
  }
  if (las[sh1[pn]] > 0) {
    mop[pn]++;
    strcpy(opt[pn][mop[pn]], "FIRE AT");
    if (thr[sh1[pn]] > 0) {
      mop[pn]++;
      strcpy(opt[pn][mop[pn]], "ATTACK ");
    }
  }
  if (expl[sh1[pn]] > 0) {
    mop[pn]++;
    strcpy(opt[pn][mop[pn]], "EXPLODE");
    if (thr[sh1[pn]] > 0) {
      mop[pn]++;
      strcpy(opt[pn][mop[pn]], "HIT    ");
    }
  }
  if (dec[sh1[pn]] > 0) {
    mop[pn]++;
    strcpy(opt[pn][mop[pn]], "DECOY  ");
  }
  if (ndoc[sh1[pn]] > 0) {
    mop[pn]++;
    strcpy(opt[pn][mop[pn]], "LAUNCH ");
  }
}

void check_con(int shp) {
  /* Detach from temporary control? */
  if (con[shp] == 255) con[shp] = 0;
}

void process(int pn, int sel) {
  /* Process selected option */
  int s;

  /*-fprintf(debug, "Option=%s\n", opt[pn][sel]);-*/
  if (!strcmp(opt[pn][sel], "STATUS ")) {
    mode[pn] = 1;
    bbc_vdu(4);
    bbc_vduq(28, 32, 31-16*pn, 39, 16-16*pn);
    bbc_colour(129+pn);
    bbc_cls();
    return;
  }
  if (!strcmp(opt[pn][sel], "DRIFT  ")) {
    act[sh1[pn]] = 0;
    mode[pn] = 1;
    bbc_vdu(4);
    bbc_vduq(28, 32, 31-16*pn, 39, 16-16*pn);
    bbc_colour(129+pn);
    bbc_cls();
    return;
  }
  if (!strcmp(opt[pn][sel], "STOP   ")) {
    act[sh1[pn]] = 1;
    mode[pn] = 1;
    bbc_vdu(4);
    bbc_vduq(28, 32, 31-16*pn, 39, 16-16*pn);
    bbc_colour(129+pn);
    bbc_cls();
    return;
  }
  if (!strcmp(opt[pn][sel], "MOVE TO")) {
    next[pn] = 2;
    mode[pn] = 3;
    bbc_vdu(4);
    bbc_vduq(28, 32, 31-16*pn, 39, 16-16*pn);
    bbc_colour(129+pn); bbc_colour(0);
    bbc_cls();
    bbc_stringprint("MOVE    TO      WHERE? ");
    return;
  }
  if (!strcmp(opt[pn][sel], "ORBIT  ")) {
    act[sh1[pn]] = 3;
    mode[pn] = 1;
    bbc_vdu(4);
    bbc_vduq(28, 32, 31-16*pn, 39, 16-16*pn);
    bbc_colour(129+pn);
    bbc_cls();
    return;
  }
  if (!strcmp(opt[pn][sel], "APPROCH")) {
    next[pn] = 4;
    mask[pn] = 3;
    mode[pn] = 4;
    bbc_vdu(4);
    bbc_vduq(28, 32, 31-16*pn, 39, 16-16*pn);
    bbc_colour(129+pn); bbc_colour(0);
    bbc_cls();
    bbc_stringprint("APPROACHWHAT    SHIP?  ");
    return;
  }
  if (!strcmp(opt[pn][sel], "MATCH  ")) {
    next[pn] = 5;
    mask[pn] = 3;
    mode[pn] = 4;
    bbc_vdu(4);
    bbc_vduq(28, 32, 31-16*pn, 39, 16-16*pn);
    bbc_colour(129+pn); bbc_colour(0);
    bbc_cls();
    bbc_stringprint("MATCH   WHAT    SHIP?  ");
    return;
  }
  if (!strcmp(opt[pn][sel], "AVOID  ")) {
    next[pn] = 6;
    mask[pn] = 3;
    mode[pn] = 4;
    bbc_vdu(4);
    bbc_vduq(28, 32, 31-16*pn, 39, 16-16*pn);
    bbc_colour(129+pn); bbc_colour(0);
    bbc_cls();
    bbc_stringprint("AVOID   WHAT    SHIP?  ");
    return;
  }
  if (!strcmp(opt[pn][sel], "DOCK   ")) {
    next[pn] = 7;
    mask[pn] = pn+1;
    mode[pn] = 4;
    bbc_vdu(4);
    bbc_vduq(28, 32, 31-16*pn, 39, 16-16*pn);
    bbc_colour(129+pn); bbc_colour(0);
    bbc_cls();
    bbc_stringprint("DOCK    WITH    WHAT    SHIP?  ");
    return;
  }
  if (!strcmp(opt[pn][sel], "FIRE AT")) {
    next[pn] = 10;
    mask[pn] = (1-pn)+1;
    mode[pn] = 4;
    bbc_vdu(4);
    bbc_vduq(28, 32, 31-16*pn, 39, 16-16*pn);
    bbc_colour(129+pn); bbc_colour(0);
    bbc_cls();
    bbc_stringprint("FIRE AT WHAT    SHIP?  ");
    return;
  }
  if (!strcmp(opt[pn][sel], "ATTACK ")) {
    next[pn] = 8;
    mask[pn] = (1-pn)+1;
    mode[pn] = 4;
    bbc_vdu(4);
    bbc_vduq(28, 32, 31-16*pn, 39, 16-16*pn);
    bbc_colour(129+pn); bbc_colour(0);
    bbc_cls();
    bbc_stringprint("ATTACK  WHAT    SHIP?  ");
    return;
  }
  if (!strcmp(opt[pn][sel], "HIT    ")) {
    next[pn] = 9;
    mask[pn] = (1-pn)+1;
    mode[pn] = 4;
    bbc_vdu(4);
    bbc_vduq(28, 32, 31-16*pn, 39, 16-16*pn);
    bbc_colour(129+pn); bbc_colour(0);
    bbc_cls();
    bbc_stringprint("HIT WHATSHIP?  ");
    return;
  }
  if (!strcmp(opt[pn][sel], "EXPLODE")) {
    /*-fprintf(debug, "Exploding\n");-*/
    mode[pn] = 1;
    act[sh1[pn]] = -1000;
    explode(sh1[pn]);
    bbc_vdu(4);
    bbc_vduq(28, 32, 31-16*pn, 39, 16-16*pn);
    bbc_colour(129+pn);
    bbc_cls();
    return;
  }
  if (!strcmp(opt[pn][sel], "DECOY  ")) {
    mode[pn] = 1;
    fire_decoy(sh1[pn]);
    bbc_vdu(4);
    bbc_vduq(28, 32, 31-16*pn, 39, 16-16*pn);
    bbc_colour(129+pn);
    bbc_cls();
    return;
  }
  if (!strcmp(opt[pn][sel], "LAUNCH ")) {
    /*-fprintf(debug, "Launching ship\n");-*/
    mode[pn] = 1;
    s = -1;
    do s++; while (act[s] != -sh1[pn]);
    launch(s, sh1[pn]);
    /*-fprintf(debug, "Launched ship %d from %d\n", s, sh1[pn]);-*/
    sh1[pn] = s;
    bbc_vdu(4);
    bbc_vduq(28, 32, 31-16*pn, 39, 16-16*pn);
    bbc_colour(129+pn);
    bbc_cls();
    return;
  }
}

void menu(int pn) {
  /* Show position in menu or process option */
  int b;

  bbc_vdu(4);
  bbc_vduq(28, 32, 31-16*pn, 39, 16-16*pn);
  bbc_colour(1+pn); bbc_colour(128);
  /* Remove previous pointer? */
  if (op[pn] != -1) {
    bbc_tab(0, op[pn]);
    bbc_vdu(32);
  }
  /* Select option? (was at end of routine) */
  b = but(pn);
  if (b) {
    if (ptrx[pn] < 380) {
      check_con(sh1[pn]);
      mode[pn] = 0;
      bbc_vdu(4);
      bbc_vduq(28, 32, 31-16*pn, 39, 16-16*pn);
      bbc_colour(128);
      bbc_cls();
      return;
    }
  }
  /* Get new option position */
  if (ptrx[pn] < 380) {
    op[pn] = -1;
  } else {
    op[pn] = 31 - ((320 + ptry[pn]) / 32) - 16 * (pn ^ 1);
  }
  if (op[pn] < 0 || op[pn] > mop[pn]) {
    op[pn] = -1;
  } else {
    /* Mark new position */
    bbc_tab(0, op[pn]);
    vdu(point);
  }
  /* Process option? */
  if (b) {
    process(pn, op[pn]);
  }
}

int middle_but(int pn) {
  /* Determine whether middle mouse button is depressed */
  _kernel_swi_regs r;

  switch(mouse[pn]) {
    case 3:
    case 4:
      _kernel_swi(0x1c, &r, &r);
      ptrx[pn] = r.r[0];
      ptry[pn] = r.r[1];
      if (r.r[2] & 2) return TRUE;
      break;
  }
  return FALSE;
}

void cursors(void) {
  /* Move players' cursors and display information in windows */
  int pn, s;
  char s1[9], s2[9];

  for (pn=0; pn<2; pn++) {
    /* Erase cursors */
    if (mode[pn] != -1) {
      gcol(3, pn+1);
      move(xcur1[pn], ycur1[pn]);
      bbc_vdu(5);
      bbc_vdu(cur1[pn]);
    } else {
      mode[pn] = 0;
    }

    /* Test and alter cursor mode */
    switch (mode[pn]) {
      case 3:
        /* Selecting position */
        cur1[pn] = cross;
        if (but(pn)) {
          datx[sh1[pn]] = ptrx[pn];
          daty[sh1[pn]] = ptry[pn];
          act[sh1[pn]] = next[pn];
          mode[pn] = 1;
          bbc_vdu(4);
          bbc_vduq(28, 32, 31-16*pn, 39, 16-16*pn);
          bbc_colour(129+pn);
          bbc_cls();
        }
        break;
      case 4:
        /* Selecting target ship */
        cur1[pn] = cross;
        if (but(pn)) {
          tg[sh1[pn]] = nr_ship(mask[pn], sh1[pn], pn);
          act[sh1[pn]] = next[pn];
          mode[pn] = 1;
          bbc_vdu(4);
          bbc_vduq(28, 32, 31-16*pn, 39, 16-16*pn);
          bbc_colour(129+pn);
          bbc_cls();
          if (middle_but(pn)) {
            for (s=0; s<nship; s++) {
              if (act[s] >= 0 && own[s] == pn && s != sh1[pn] && con[s]>0) {
                strcpy(s1, sp_strncpy(ship[s], 8));
                strcpy(s2, sp_strncpy(ship[sh1[pn]], 8));
                if (strcmp(s1, s2) == 0) {
                  tg[s] = tg[sh1[pn]];
                  act[s] = act[sh1[pn]];
                }
              }
            }
          }
        }
        break;
      case 2:
        /* Menu */
        cur1[pn] = square;
        menu(pn);
        break;
      case 1:
        /* Attached, showing status. Detach from ship or go to menu? */
        cur1[pn] = square;
        if (but(pn)) {
          if (cur2on[pn]) {
            gcol(3, 1+pn);
            move(xcur2[pn], ycur2[pn]);
            bbc_vdu(5); bbc_vdu(cur2[pn]);
            cur2on[pn] = FALSE;
          }
          if (ptrx[pn] < 380 || con[sh1[pn]] == 0) {
            check_con(sh1[pn]);
            mode[pn] = 0;
          } else {
            mode[pn] = 2;
            op[pn] = -1;
            create_menu(pn);
          }
          bbc_vdu(4);
          bbc_vduq(28, 32, 31-16*pn, 39, 16-16*pn);
          bbc_colour(129+pn);
          bbc_cls();
        }
        break;
      case 0:
        /* Free movement. Attach to ship? */
        cur1[pn] = square;
        if (but(pn)) {
          mode[pn] = 1;
          sh1[pn] = nr_ship(pn+1, -1, pn);
          if (sh1[pn] == -1) player_lost(pn);
          bbc_vdu(4);
          bbc_vduq(28, 32, 31-16*pn, 39, 16-16*pn);
          bbc_colour(129+pn);
          bbc_cls();
        }
        break;
    }

    /* Get new position */
    if (mode[pn] == 0 || mode[pn] == 3 || mode[pn] == 4) {
      read_cursor_position(pn);
    } else {
      xcur1[pn] = (int) x[sh1[pn]];
      ycur1[pn] = (int) y[sh1[pn]];
    }
    /* Show cursor */
    gcol(3, 1+pn);
    move(xcur1[pn], ycur1[pn]);
    bbc_vdu(5); bbc_vdu(cur1[pn]);

    /* Move dummy mouse */
    if (mouse[pn] == 1) {
    }
  }
}

void show_menus(void) {
  /* Display menus if required */
  int pn, o;
  for (pn=0; pn<2; pn++) {
    if (mode[pn] == 2) {
      bbc_vdu(4);
      bbc_vduq(28, 33, 31-16*pn, 39, 16-16*pn);
      bbc_colour(129+pn); bbc_colour(0);
      bbc_cls();
      for (o=0; o<=mop[pn]; o++) {
        printf("%s\n", opt[pn][o]);
      }
    }
  }
}

void initialise_mice(void) {
  FILE *f;

  if (f = fopen("<Space$Dir>.Choices", "rb"), f == NULL) {
    printf("Unable to open Choices file.");
    exit(1);
  }
  mouse[0] = fgetc(f) - '0';
  mouse[1] = fgetc(f) - '0';
  /* Mouse values have the following significance:
   *   1 - Keypad controlled pointer
   *   2 - Left keyboard controlled pointer
   *   3 - Normal mouse, select
   *   4 - Normal mouse, adjust
   */
  fclose(f);
}

void exit_proc(void) {
  if (debug != NULL) {
    fclose(debug);
  }
}

int main() {
  int j, t, tn;
  double or, ov, angle;

  /* Open debug file */
  if (debug = fopen("<Space$Dir>.Debug", "a+"), debug == NULL) {
    fprintf(stderr, "Unable to open debug file");
    exit(1);
  }
  /* Register exit handler to close debug file */
  if (atexit(exit_proc)) {
    fclose(debug);
    fprintf(stderr, "Unable to register exit handler");
    exit(1);
  }
  /*-fprintf(debug, "\nStarted Battle\n");-*/

  /* Seed RNG */
  Rand_state_init(bbc_time());
  /*-fprintf(debug, "Seeded RNG\n");-*/

  /* Initialise variables */
  for (i=0; i<2; i++) {
    xcur1[i] = -10000;
    xcur2[i] = -10000;
    ycur1[i] = 0;
    ycur2[i] = 0;
    mode[i] = -1;
    press[i] = FALSE;
    cur1[i] = 32;
    cur2[i] = 32;
    cur2on[i] = FALSE;
  }
  initialise_mice();
  /*-fprintf(debug, "Variable initialisation successful\n");-*/
  /*-fprintf(debug, "mouse[0]=%d, mouse[1]=%d\n", mouse[0], mouse[1]);-*/

  /* Set required screen mode and colours */
  bbc_vduq(22,1);
  bbc_vduq(19,1,6,0,0,0);
  bbc_vduq(19,2,5,0,0,0);

  /* Set up graphics area with border */
  gcol(0, 3);
  draw_rect(0, 0, 1023, 1023);
  /* Graphics window */
  bbc_vdu(24); bbc_vduw(4); bbc_vduw(4); bbc_vduw(1019); bbc_vduw(1019);
  bbc_vdu(29); bbc_vduw(640); bbc_vduw(320);

  /* Draw planet */
  gcol(0, 1);
  for (i=-radius; i<=radius; i+=4) {
    j = (int) sqrt(((double)(radsq) - (double)(SQR((double)i))));
    draw_line(i+16, -j-16, i+16, j-16);
  }

  /* Define characters for cursors, etc. */
  bbc_vduq(23, square, 255, 129, 129, 129, 129, 129, 129, 255);
  bbc_vduq(23, point, 8, 252, 254, 255, 254, 252, 8, 0);
  bbc_vduq(23, cross, 0x10, 0x10, 0x10, 0xfe, 0x10, 0x10, 0x10, 0);
  bbc_vduq(23, explos, 0x18 & randint(256), 0x7e & randint(256), 0x7e & randint(256), 0xff & randint(256), 0xff & randint(256), 0x7e & randint(256), 0x7e & randint(256), 0x18 & randint(256));
  bbc_vduq(23, decoy, 0, 0, 0, 0x10, 0, 0, 0, 0);
  /*-fprintf(debug, "Characters defined\n");-*/

  /* Read in players forces and create decoys */
  read_force_files();
  /*-fprintf(debug, "Forces read in\n");-*/

  /* Copy initial values of ship attributes */
  for (i=0; i<nship; i++) {
    j = i+nship;
    con[j] = con[i];
    gen[j] = gen[i];
    sto[j] = sto[i];
    thr[j] = thr[i];
    las[j] = las[i];
    expl[j] = expl[i];
    shi[j] = shi[i];
    arm[j] = arm[i];
    dec[j] = dec[i];
    doc[j] = doc[i];
  }

  /* Determine initial positions and velocities */
  for (i=0; i<nship; i++) {
    if (act[i] >= 0) {
      if (own[i] == 0) {
        or = (2.0 + (double)(rand_int(1000)/1000.0)) * (double)radius;
        ov = 2.0 * (double)((double)rand_int(2) - 0.5) * sqrt(g / or);
        angle = rand_int(360) / (2*PI);
        x[i] = or * cos(angle);
        y[i] = or * sin(angle);
        u[i] = ov * y[i] / or;
        v[i] = -ov * x[i] / or;
      } else {
        x[i] = (double)rand_int(256) + 1.0 - 640.0;
        y[i] = 1024.0 - 320.0 - (double)rand_int(256) + 1.0;
        u[i] = (double)rand_int(6) + 1.0;
        v[i] = -((double)rand_int(6) + 1.0);
      }
      gcol(3, 1+own[i]);
      moveD(x[i], y[i]);
      bbc_vdu(5);
      bbc_vdu(chr[i]);
      bbc_vdu(4);
    }
  }

  /* Determine time step */
  tick = min(0.9, 0.025 * nship);

  /* Switch off default cursor */
  bbc_cursor(0);

  /* Wait for possible mode fade-in */
  wait(150);

  /* Main game loop */
  do {
    t = bbc_time();
    for (i=0; i<nship; i++) {
      do_ship();
      cursors();
      show_menus();
    }
    if (bbc_time() - tick * 100 > 20) {
      wait(10);
      cursors();
      show_menus();
    }
    do {
      /* cursors();
      show_menus(); */
      tn = bbc_time();
    } while (((double)tn - (double)t) <= tick * 100);
  } while (TRUE);

}
/* END */
