#include "c_includ.h"
/*
 * Cbzone - xbzone w/ improvements in C
 *
 * Version 1.0 in Fortran by Justin S. Revenaugh -- MIT (5/86)
 * C port and modifications by Todd W. Mummert -- CMU (12/90)
 *   email bugs and comments concerning this game to:
 *                 mummert+@sam.cs.cmu.edu
 *
 * Copyright Notice:  This program is freely distributable on a
 * nonprofit basis as long as this notice is maintained on all
 * copies. Inclusion of any program code or derivative thereof
 * for commercial purposes is expressly forbidden.
 * December 10, 1990.
 *
 * RCS Info
 *  $Header: c_main.c,v 1.1 91/01/12 02:03:34 mummert Locked $
 *
 * Bugs/Features:
 *   Tanks still pass through each other.  Missiles/copters don't
 *   collide with one another unless below 80 ft.  Consider these
 *   features for now.
 *
 * Enhancement Ideas:
 *  -Convert the graphics routines to general purpose routines...
 *   The graphics are either multiline(segments) or polyline(connected).
 *   Polyline lines can either be open or closed.  This could all be
 *   done in a single routine with the number of mlines and plines
 *   passed in...maybe too much bookkeeping involved.
 *  -Have the ability to have both tanks and missiles on the screen at
 *   the same time.  Never bothered to do it, for I initially thought
 *   it would be unplayable.  Not so sure now.
 *  -Make this multiplayer...two basic approaches.  The first is one
 *   program controlling two displays.  The second is running on
 *   different machines, passing the necessary objects back/forth.  Since
 *   you wouldn't need to pass graphics information, this is not
 *   a lot of information to update. Instead of a call to move<enemy>
 *   you would get back the changes made by your opponent.
 *
 *  -If you make any interesting changes, please send them to me and I'll
 *   incorporate them into the next release.
 */


Option option;
Optionp opt = &option;

/*
 * Just initialize the array prior to starting play.  Associate
 * the salvos with the correct objects.  Set the cosines of some
 * of the angles to 1.
 */
void initarray(o)
     Genericp o;
{
  Genericp s;
  Genericp g;
  Genericp pl = o;
  int i, j;

  pl->type = IS_PLAYER;                 /* player is always the first */
  pl->attr = START_LIVING;              /* object.  Same size as the */
  pl->criticalx = 45.0;                 /* other tanks.  */
  pl->criticaly = 70.0;
  for (g=o; g<o+opt->mobjects; g++) {
    g->ca = 1.0;                        /* for those objects which */
    for (j=0; j<5; j++)                 /* have rotating parts, set */
      g->dc[j].ctp = g->dc[j].cta = 1.0;  /* the cos to 1.0 */
  }

  s = o+opt->sstart;                    /* player gets msalvos, each  */
  pl->salvo = s;                        /* enemy gets one.  the salvos */
  for (i=0; i<opt->msalvos; i++) {      /* must know who their owner */
    s->salvo = pl;                      /* is for a variety of reasons */
    s->type = IS_SALVO;                 /* first the player */
    s->lntype = LN_SALVO;
    s++;
  }
  for (g=o+opt->estart; g<o+opt->lstart; g++) {
    g->salvo = s;                       /* now the enemies */
    s->salvo = g;
    s->type = IS_SALVO;
    s->lntype = LN_SALVO;
    s++;
  }
}

/*
 * The main routine for cbzone.  Probably more complicated than it
 * need be...but it takes care of all the interobject dependencies.
 * Therefore other routines work on a single object (in general).
 * placeobjects() will place however many objects it can, while
 * scanner() needs to know where all the enemies are.
 */
void cbzone_main(argc, argv)
     int argc;
     char* argv[];
{
  Genericp o;
  Genericp pl, g, g2, s;
  char key;
  float alpha, ddx2, ddx, ddy2, ddy, dif, diff, dx, dy, v, dist;
  float blocksize = 100.0;
  float blocksizesqrd = 10000.0;
  float check, testx, testy;
  float landerthreshold = 0.5;
  float threshold = 0.7;
  int deadcount, i, icheck, position[2];
  int nummissile = 0;
  int missilecount = 0;
  int nextmissile = 1200;
  int numleft = 3;
  LONG score = 0;
  LONG scorebase = 0;
  Bool new_salvo_flag, new_sight_flag, event, tank_stranded;
  Bool aligned = False;
  Bool blocked_flag = False;
  Bool dead = False;
  Bool first = True;
  Bool firstmissile = True;
  Bool keylast = True;
  Bool lander = False;
  Bool missilerun = False;
  Bool salvo_flag = False;
  Bool sens = False;
  Bool sight_flag = False;
//  extern long time();  ***HACK HACK HACK EricFo removed this
  struct timeval tstart;
  struct timeval tend;
  long tdiff, limit;
#ifdef DEVELOPER
  int passes = 0;
  struct timeval game_start;
  struct timeval game_end;
#endif //DEVELOPER

  gprinqconfig(&argc, argv);
  limit = opt->delay * 1.2e4;

  /* now that we have parsed the options, we know how large to */
  /* make the world.  Use calloc here as most of the array     */
  /* should start off 0.                                       */

  pl = o = (Genericp) calloc(opt->mobjects,sizeof(Generic));

  if (o == NULL) {
    printf("Malloc failed...trying to create too many objects?\n");
#ifdef WIN32
     return;
#else //X11
     exit(1);
#endif
  }

  initarray(o);                         /* prepare the main array */
  srandom(time((long *) 0));            /* start things off randomly */
  screeninit();
  updatedisplay(missilerun, lander, score, numleft, sens, False);
  xhairs(aligned);
  gprinqcursor(position);
  event = gprcondeventwait(&key, position);
  joystick(position, sens, pl);

  /* place the objects out there to start the game.  if the player is */
  /* is_new, then the objects are placed at a random distance, else   */
  /* the objects get placed on the horizon.                           */

  placeobjects(o, missilerun, score);
  pl->attr &= ~IS_NEW;                  /* now the objects can be */
                                        /* placed at the horizon. */

  /* now calculate ranges to all the objects and translate them */
  /* into a player-centric coordinate system                    */

  pl->ca = cos(pl->azm);
  pl->sa = sin(pl->azm);
  for (g=o+opt->estart; g<o+opt->mobjects; g++)
    if (g->attr & IS_ALIVE) {
      dx = g->x - pl->x;
      dy = g->y - pl->y;
      g->range = sqrt(dx*dx + dy*dy);
      g->proy = -dx * pl->sa + dy * pl->ca;
      g->prox = dx * pl->ca + dy * pl->sa;
    }

  scanner(o);
  drawhorizon(pl->azm);

  /* now the work really starts....we just iterate through */
  /* the following loop until the player dies or quits     */
#ifdef DEVELOPER
  gettimeofday(&game_start, 0);
#endif
  while (1) {
#ifdef WIN32
    if (GetAsyncKeyState( VK_F1 ) < 0) {
      free(o);
      return;
//      exit(scores(score));  //HACK OUT by Eric Fogelin
    }
#endif
    gettimeofday(&tstart, 0);
    gprinqcursor(position);
    event = gprcondeventwait(&key, position);

    if (event && key == 'Q') {
      free(o);
      return;
//      exit(scores(score));  //HACK OUT by Eric Fogelin
    }

    if (event && key == 'R') {
      clearentirescreen();
      staticscreen();
      updatedisplay(False, False, -1, 0, False, True);
      updatedisplay(missilerun, lander, score, numleft, sens, False);
      if (sight_flag)
        message(1, False);
      if (pl->attr & IS_BLOCKED)
        message(2, False);
      if (salvo_flag)
        message(3, False);
      scanner(o);
      xhairs(aligned);
      drawhorizon(pl->azm);
    }

    joystick(position, sens, pl);
    if (paused)
      continue;

    for (i=0; i<opt->msalvos; i++) {    /* now find a shot we can use */
      s = pl->salvo+i;
      if (!(s->attr & STILL_THERE))
        break;
      s = NULL;
    }
    if (event && pl->attr & IS_ALIVE)
      if (keylast) {
        for (i=0; i<opt->msalvos; i++) { /* now find a shot we can use */
          s = pl->salvo+i;
          if (!(s->attr & STILL_THERE))
            break;
          s = NULL;
        }
        if (key == 'a' && s!=NULL) {    /* fire up one shot */
          s->attr = START_LIVING;
          s->ecount = 0;
          s->x = pl->x;
          s->y = pl->y;
          s->z = 0.0;
          s->prox = 0;
          s->proy = 0;
          s->azm = pl->azm;
          s->speed = 40.0;
          keylast = False;
        }
        else if (key == 'b') {          /* center our joystick */
          position[0] = 500;
          position[1] = 355;
          gprsetcursorposition(position);
          joystick(position, sens, pl);
          keylast = False;
        }
        else if (key == 'c') {          /* toggle sensitivity */
          sens = !sens;
          joystick(position, sens, pl);
          keylast = False;
        }
      }
      else if (key == 'A' || key == 'B' || key == 'C')
        keylast = True;                 /* button released */

    /* if we can move, update our rotation, bearing (azimuth), and */
    /* position.                                                   */

    if (pl->attr & IS_ALIVE && !(pl->attr & IS_BLOCKED)) {
      pl->azm += pl->rotate;
      if (pl->azm > PI2)
        pl->azm -= PI2;
      if (pl->azm <= 0.0)
        pl->azm += PI2;
      pl->ca = cos(pl->azm);
      pl->sa = sin(pl->azm);
    }
    if (pl->attr & IS_ALIVE) {
      pl->x -=  pl->sa * pl->speed;
      pl->y +=  pl->ca * pl->speed;
    }
    else
      pl->speed = 0.0;

    /* now call the move generation routines for the objects */
    /* which require thought,  speed and/or rotation may be  */
    /* affected.                                             */

    for (g=o+opt->estart; g<o+opt->sstart; g++)
      if (g->attr & IS_ALIVE)
        switch (g->type) {
        case IS_TANK:
          movetank(g, pl); break;
        case IS_SUPER:
          movesuper(g, pl); break;
        case IS_MISSILE:
          movemissile(g, pl, first); break;
        case IS_COPTER:
          movecopter(g, pl); break;
        case IS_LANDER:
          movelander(g, pl); break;
        default:
          printf("Help! Something's alive and I don't know what...\n");
#ifdef WIN32
          return;
#else //X11
          exit(1);
#endif
        }

    /* now update their bearing and position */

    for (g=o+opt->estart; g<o+opt->lstart; g++) {
      if (g->attr & IS_ALIVE && !(g->attr & IS_BLOCKED))
        g->azm += g->rotate;
      g->ca = cos(g->azm);
      g->sa = sin(g->azm);
      g->x -=  g->sa * g->speed;
      g->y +=  g->ca * g->speed;
    }
    for (g=o+opt->lstart; g<o+opt->bstart; g++)
      if (g->attr & IS_ALIVE) {
        g->ca = cos(g->azm);
        g->sa = sin(g->azm);
        g->x -= g->sa * g->speed;
        g->y += g->ca * g->speed;
      }

    /* now compute ranges from objects to the player */

    for (g=o+opt->estart; g<o+opt->mobjects; g++)
      if (g->attr & STILL_THERE)
        g->range = sqrt(DIST(g, pl));

    for (g=o; g<o+opt->lstart; g++)     /* assume all objects are */
      g->attr &= ~IS_BLOCKED;           /* unblocked              */

    /* now check to see if they really were unblocked.  If not, then */
    /* project them back along their path until they are.  This      */
    /* section just checks for being blocked by blocks.              */

    for (g=o+opt->bstart; g<o+opt->mobjects; g++) {
      if (g->range < blocksize) {
        pl->attr |= BLOCKED_BY_BLOCK;
        dx = pl->x - g->x;
        dy = pl->y - g->y;
        diff = dy * pl->ca - dx * pl->sa;
        if (pl->speed > 0.0)
          v = diff + sqrt(diff*diff + blocksizesqrd - g->range*g->range);
        else if (pl->speed < 0.0)
          v = diff - sqrt(diff*diff + blocksizesqrd - g->range*g->range);
        pl->x += pl->sa * v;
        pl->y -= pl->ca * v;
      }
      for (g2=o+opt->estart; g2<o+opt->lstart; g2++)
        if (g2->attr & IS_ALIVE &&
            (dist = DIST(g, g2)) < blocksizesqrd) {
          g2->attr |= BLOCKED_BY_BLOCK;
          if (!(g2->type & (IS_MISSILE | IS_COPTER))) {
            dx = g2->x - g->x;
            dy = g2->y - g->y;
            diff = dy * g2->ca - dx * g2->sa;
            if (g2->speed > 0.0)
              v = diff + sqrt(diff*diff + blocksizesqrd - dist);
            else if (g2->speed < 0.0)
              v = diff - sqrt(diff*diff + blocksizesqrd - dist);
            g2->x += g2->sa * v;
            g2->y -= g2->ca * v;
          }
        }
    }

    /* if the player moved, or if an enemy did, we need to recompute */
    /* the range to that enemy.                                      */

    for (g=o+opt->estart; g<o+opt->lstart; g++)
      if (g->attr & IS_ALIVE &&
          (g->attr & IS_BLOCKED || pl->attr & IS_BLOCKED))
        g->range = sqrt(DIST(g, pl));

    /* now check to see if the player is blocked by any enemy.  */
    /* if so, project them back.                                */

    for (g=o+opt->estart; g<o+opt->lstart; g++)
      if (g->attr & IS_ALIVE && g->range < blocksize)
        if (!(g->type & (IS_MISSILE | IS_COPTER))) {
          if (g->attr & IS_BLOCKED) {
            pl->speed = 0.0;
            if (fabs(g->speed) < 0.001)
              g->speed = sign(0.001, g->speed);
          }
          pl->attr |= BLOCKED_BY_ENEMY;
          g->attr |= BLOCKED_BY_ENEMY;
          ddx = pl->speed * pl->sa - g->speed * g->sa;
          ddy = pl->speed * pl->ca - g->speed * g->ca;
          ddx2 = ddx*ddx;
          ddy2 = ddy*ddy;
          dx = pl->x - g->x;
          dy = pl->y - g->y;
          dif = ddy * dy - ddx * dx;
          alpha = (dif + sqrt(dif*dif + (blocksizesqrd - g->range*g->range)
                              * (ddx2 + ddy2))) / (ddx2 + ddy2);
          pl->x += alpha * pl->speed * pl->sa;
          pl->y -= alpha * pl->speed * pl->ca;
          g->x += alpha * g->speed * g->sa;
          g->y -= alpha * g->speed * g->ca;
        }

    /* if we've moved, recompute distance to all the salvos */

    if (pl->attr & IS_BLOCKED)
      for (g=o+opt->sstart; g<o+opt->bstart; g++)
        if (g->attr & IS_ALIVE)
          g->range = sqrt(DIST(g, pl));

    /* enemies disappear if their range is greater than 2200. */
    /* We check last[0] to see if they need to be erased.  In */
    /* most cases probably not, unless we really screw with   */
    /* their speed.                                           */

    tank_stranded = False;
    for (g=o+opt->estart; g<o+opt->lstart; g++) {
      g->ecount++;
      if (g->attr & IS_ALIVE)
        if (g->range > 2200.0)
          if (g->dc[0].last)
            g->attr = ERASE;
          else
            g->attr = 0;
        else if (g->type & (IS_SUPER | IS_TANK) &&
                 g->ecount > TANK_STRAND_COUNT)
          tank_stranded = True;
    }

    /* landers are out of range at 2750 */

    for (g=o+opt->lstart; g<o+opt->sstart; g++) {
      g->ecount++;
      if (g->attr & IS_ALIVE && g->range > 2750.0)
        if (g->dc[0].last)
          g->attr = ERASE;
        else
          g->attr = 0;
    }

    /* blocks also at 2200 */

    for (g=o+opt->bstart; g<o+opt->mobjects; g++)
      if (g->range > 2200.0)
        if (g->dc[0].last)
          g->attr = ERASE;
        else
          g->attr = 0;

    /* salvos are never out of range, but their lifetime is limited */

    for (g=o+opt->sstart; g<o+opt->bstart; g++) {
      g->ecount++;
      if (g->attr & IS_ALIVE && g->ecount > 50)
        if (g->dc[0].last)
          g->attr = ERASE;
        else
          g->attr = 0;
    }

    /* we never set the 'salvo fired' message in this routine.  */
    /* however, we do have to turn it off.  if salvos are alive */
    /* we assume the message is on, once no enemy salvos are    */
    /* alive we turn it off.                                    */

    new_salvo_flag = False;
    for (g=o+opt->sstart; g<o+opt->bstart; g++)
      if (g->attr & IS_ALIVE) {         /* if salvo exist and   */
        if (g->salvo != pl)             /* not owned by player  */
          new_salvo_flag = True;        /* then the flag is set */

        /* check to see if a salvo hits a block */

        for (g2=o+opt->bstart; g2<o+opt->mobjects; g2++)
          if (fabs(g2->x - g->x) < g2->criticalx &&
              fabs(g2->y - g->y) < g2->criticaly) {
            g->attr = START_EXPLODING;
            g->ecount = 0;
          }

        /* now check to see if the salvo kills a lander. */
        /* If so, and the player fired the salvo, update */
        /* the score.                                    */

        for (g2=o+opt->lstart; g2<o+opt->sstart; g2++)
          if (g2->attr & IS_ALIVE)
            if (DIST(g, g2) < g2->criticalx) {
              g->attr = START_EXPLODING;
              g2->attr = START_EXPLODING;
              g->ecount = 0;
              g2->ecount = 0;
              if (g->salvo == pl) {
                score += 10000;
                icheck = score / 100000;
                if (icheck > scorebase) {
                  numleft++;
                  if (numleft > 4)
                    numleft = 4;
                  scorebase = icheck;
                }
              }
            }

        /* now check to see if the salvo hit any enemy.   The salvo */
        /* cannot hit the one who fired it.  This prevents range    */
        /* checking problems when first fired.                      */

        for (g2=o+opt->estart; g2<o+opt->lstart; g2++)
          if (g2->attr & IS_ALIVE && g->salvo != g2) {
            dx = g->x - g2->x;
            dy = g->y - g2->y;
            testx = fabs( dx * g2->ca + dy * g2->sa);
            testy = fabs(-dx * g2->sa + dy * g2->ca);
            if (testx < g2->criticalx && testy < g2->criticaly &&
                (!(g2->type & (IS_MISSILE | IS_COPTER)) || g2->z < 80.0)) {
              g->attr = START_EXPLODING;
              g2->attr = START_EXPLODING;
              g->ecount = 0;
              g2->ecount = 0;

              /* if the player fired, give him credit */

              if (g->salvo == pl) {
                if (g2->type & IS_SUPER)
                  score += 5000;
                else if (g2->type & (IS_MISSILE | IS_COPTER)) {
                  score += 5000;
                  nummissile--;
                }
                else
                  score += 3000;
                icheck = score / 100000;
                if (icheck > scorebase) {
                  numleft++;
                  if (numleft > 4)
                    numleft = 4;
                  scorebase = icheck;
                }
              }
            }
          }
      }

    /* check to see if a missile or copter rams a lander */

    for (g=o+opt->estart; g<o+opt->lstart; g++)
      if (g->type & (IS_MISSILE | IS_COPTER) && g->attr & IS_ALIVE)
        for (g2=o+opt->lstart; g2<o+opt->sstart; g2++)
          if (g2->attr & IS_ALIVE &&
              DIST(g, g2) < g2->criticalx && g->z < 80) {
            nummissile--;
            g->attr = START_EXPLODING;
            g2->attr = START_EXPLODING;
            g->ecount = 0;
            g2->ecount = 0;
          }

    /* now check if we need to draw the object.  */
    /* convert into player-centric coordinates   */
    /* and project a cone forward to see if the  */
    /* enemy is within it. Also align the gun    */
    /* gun sights if necessary.                  */

    new_sight_flag = False;
    aligned = False;
    lander = False;
    for (g=o+opt->estart; g<o+opt->mobjects; g++)
      if (g->attr & (IS_ALIVE|IS_EXPLODING)) {
        g->dc[0].seen = False;
        if (g->range < 2000.0) {
          dx = g->x - pl->x;
          dy = g->y - pl->y;
          g->proy = -dx * pl->sa + dy * pl->ca;
          g->prox =  dx * pl->ca + dy * pl->sa;
          check = g->proy / (fabs(g->prox) + 1.0);
          if (check > threshold) {
            g->dc[0].seen = True;
            if (g->type & IS_ENEMY && g->attr & IS_ALIVE)
              new_sight_flag = True;
            if (fabs(g->prox) < 50 && g->attr & IS_ALIVE &&
                !(g->type & (IS_ABLOCK | IS_SALVO)))
              aligned = True;
          }
          if (g->type & IS_LANDER && g->attr & IS_ALIVE
              && check > landerthreshold) {
            lander = True;
            if (fabs(g->prox) < 60)
              aligned = True;
          }
        }
      }

    /* change the various messages, if necessary.  Never use */
    /* the bell, unless opt->loud is True.                   */

    if (sight_flag && !new_sight_flag) {
      message(-1, False);
      sight_flag = False;
    }
    else if (!sight_flag && new_sight_flag) {
      message(1, False);
      sight_flag = True;
    }
    if (pl->attr & IS_BLOCKED && !blocked_flag) {
      message(2, True);
      blocked_flag = True;
    }
    else if (!(pl->attr & IS_BLOCKED) && blocked_flag) {
      message(-2, False);
      blocked_flag = False;
    }
    if (salvo_flag && !new_salvo_flag) {
      message(-3, False);
      salvo_flag = False;
    }
    else if (!salvo_flag && new_salvo_flag)
      salvo_flag = True;

    scanner(o);
    updatedisplay(missilerun, lander, score, numleft, sens, False);
    xhairs(aligned);
    drawhorizon(pl->azm);

    /* now draw all the objects */

    for (g=o+opt->estart; g<o+opt->mobjects; g++) {
      if (g->attr & (IS_ALIVE | ERASE))
        drawobject(g, pl);
      else if (g->attr & (IS_EXPLODING | EXERASE))
        switch (g->type) {
        case IS_SALVO:
          explodesalvo(g, pl); break;
        case IS_COPTER:
        case IS_MISSILE:
        case IS_LANDER:
        case IS_TANK:
        case IS_SUPER:
          explodeobject(g, pl); break;
        default:
          printf("Help! Cannot explode what doesn't exist.\n");
#ifdef WIN32
          return;
#else //X11
          exit(1);
#endif
        }
      g->attr &= ~(ERASE | EXERASE);
    }

    /* now start checking for player death.  if there is a missile, */
    /* check to see if it rammed the player.                        */

    if (missilerun)
      for (g=o+opt->estart; g<o+opt->lstart; g++)
        if (g->attr & IS_ALIVE && g->type & (IS_MISSILE | IS_COPTER) &&
            g->range < blocksize && g->z < 80) {
          g->attr = START_EXPLODING;
          drawcracks();
          pl->attr &= ~IS_ALIVE;
          dead = True;
          deadcount = 0;
        }

    /* check to see if any salvos hit. */

    for (g=o+opt->sstart; g<o+opt->bstart; g++)
      if (g->attr & IS_ALIVE && g->salvo != pl &&
          g->range < 100.0 && fabs(g->prox) < pl->criticalx &&
          fabs(g->proy) < pl->criticaly) {
        drawobject(g, pl);
        g->attr = 0;
        drawcracks();
        pl->attr &= ~IS_ALIVE;
        dead = True;
        deadcount = 0;
      }

    /* if we are dead, redraw the cracks every five turns.  after 50 */
    /* turns, we can start playing again if we have any lives left.  */

    if (dead) {
      if (deadcount%5 == 0)
        drawcracks();
      if (deadcount > 50) {
        dead = False;
        numleft--;
        if (numleft < 0) {
#ifdef DEVELOPER
          gettimeofday(&game_end, 0);
          if (opt->output)
            printf("The game took an average %10.8f secs.\n",
                   (game_end.tv_sec-game_start.tv_sec +
                    (game_end.tv_usec-game_start.tv_usec)*1.0e-6)/passes);
#endif //DEVELOPER
          free(o);
#ifdef WIN32
          return;
#else //X11
          exit(scores(score));
#endif
        }
        if (missilerun) {
          nummissile -= 2;
          if (nummissile <= 0 && opt->mtanks) {
            missilerun = False;
            nextmissile = 750 * frand() + 750;
          }
        }
        clearscreen();
        pl->x = 0.0;                    /* reset all our attributes */
        pl->y = 0.0;
        pl->speed = 0.0;
        pl->azm = 0.0;
        pl->ca = 1.0;
        pl->sa = 0.0;
        pl->attr = START_LIVING;
        message(-1, False);             /* turn off all the messages */
        sight_flag = False;
        message(-2, False);
        blocked_flag = False;
        message(-3, False);
        salvo_flag = False;
        for (g=o+opt->estart; g<o+opt->mobjects; g++)
          g->attr = 0;                  /* remove all objects */
      }
    }

    /* Now schedule the missile runs.  There will be a missile run if */
    /* mtanks==0 or we are in copter practice or if a tank has been   */
    /* around too long or we haven't had a missile in a while.        */

    if (pl->attr & IS_ALIVE && opt->mmissiles &&
        (!opt->mtanks || opt->copters ||
         tank_stranded || missilecount > nextmissile))
      if (!missilerun) {
        missilecount = 0;
        for (g=o+opt->estart; g<o+opt->bstart; g++)
          if (!(g->type & (IS_LANDER | IS_MISSILE | IS_COPTER)))
            if (g->attr & IS_ALIVE)
              g->attr = ERASE;
            else if (g->attr & IS_EXPLODING)
              g->attr = EXERASE;
        nummissile = frand() * 3 * opt->mmissiles + 1;
        if (firstmissile)
          nummissile = 1;
        first = firstmissile;
        firstmissile = False;
        missilerun = True;
      }

    /* once this run is over, schedule another one for a later date */

    if (missilerun && opt->mtanks && nummissile <= 0) {
      missilerun = False;
      nextmissile = 750 * frand() + 750;
    }

    /* now place whatever objects need to be placed.  */

    if (!dead)
      placeobjects(o, missilerun, score);
    pl->attr &= ~IS_NEW;                /* in case the player was new */

    deadcount++;
    if (!missilerun) missilecount++;

/* use timeclock here instead of gettimeofday to get a sync(d, 0)
 * just in case there are graphics we need to draw.
 */
    timeclock(&tend);
    tdiff = limit -
      ((tend.tv_sec-tstart.tv_sec)*1e6+tend.tv_usec-tstart.tv_usec);
    if (tdiff > 0) {
      tend.tv_sec = 0;
      tend.tv_usec = tdiff;
      select(0, 0, 0, 0, &tend);
    }
#ifdef DEVELOPER
    passes++;
#endif //DEVELOPER
  }
}
