/* 
 * missile.c -- Missile Command
 * Author          : Julian Peterson <weaver@earthcorp.com>
 * Created On      : Tue Feb 22 22:13:17 2000
 * Last Modified By: Julian Peterson <weaver@earthcorp.com>
 * Last Modified On: Tue Jun 27 21:29:48 2000
 * Update Count    : 988
 * Status          : Peachy
 * $Id: missile.c,v 0.101 2000/04/12 20:26:49 julian Exp julian $
 * 
 * Copyright (C) 2000 Julian Peterson
 * 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or (at
 * your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
 * USA
*/


#include "missile.h"


/* globals */
static int RUNNING = 1;

/* rectangle update list stuff */
#define MAX_ITEMS_TO_UPDATE 30
SDL_Rect RECTS_TO_UPDATE[MAX_ITEMS_TO_UPDATE];
int LAST_RECT = 0;


SDL_Surface *SPRITE[NUMBER_SPRITES];
SDL_Rect     IMAGE_LOCATION[NUMBER_STATIC_IMAGES];

///////////////////////////////////////////////////////////////////////////

void draw_line(SDL_Surface *screen, Game *g, Missile *m )
{
  int i;
  SDL_Rect r;
  Uint32 pixel;
  int from;

  if ( get_game_time(g) < m->created_at ) /* missile doesn't exist yet */
    return;
  
  from = m->at;
  m->at = MIN(m->path_length - 1,
              RND(m->velocity * (get_game_time(g) - m->created_at) / 1000.0));

  //printf("draw from %i to %i of max %i\n", from, m->at, m->path_length);

  if ( from+1 < m->path_length ) /* missile enroute */
  {
    pixel = SDL_MapRGB(screen->format, G_YELLOW);
    
    if ( SDL_MUSTLOCK(screen) && (SDL_LockSurface(screen) < 0) ) return;

    for (i=from; i<=m->at; i++)
      DRAWPIXEL( screen, m->path[i].x, m->path[i].y, pixel );
    
    if ( SDL_MUSTLOCK(screen) )  SDL_UnlockSurface(screen);
  
    r.x = MIN( m->path[from].x, m->path[m->at].x );  
    r.y = MIN( m->path[from].y, m->path[m->at].y );
    r.w = ABS( m->path[from].x - m->path[m->at].x ) + 1;
    r.h = ABS( m->path[from].y - m->path[m->at].y ) + 1;

    //printf("(%03i,%03i) %03ix%03i\n", r.x, r.y, r.w, r.h); 
    UpdateRect(&r);
  }
  else  /* this missiles time is over */
  {
    m->detonated = 1;
  }
}

///////////////////////////////////////////////////////////////////////////

void zap_line(SDL_Surface *screen, Missile *m)
{
  int i;
  SDL_Rect r;
  Uint32 pixel;

  pixel = SDL_MapRGB(screen->format, G_BLACK);
  
  if ( SDL_MUSTLOCK(screen) && (SDL_LockSurface(screen) < 0) ) return;

  for (i=0; i <= m->at; i++)
    DRAWPIXEL( screen, m->path[i].x, m->path[i].y, pixel );

  if ( SDL_MUSTLOCK(screen) )  SDL_UnlockSurface(screen);
  
  r.x = MIN( m->path[0].x, m->path[m->at].x );  
  r.y = MIN( m->path[0].y, m->path[m->at].y );
  r.w = ABS( m->path[0].x - m->path[m->at].x ) + 1;
  r.h = ABS( m->path[0].y - m->path[m->at].y ) + 1;
  UpdateRect(&r);

}

///////////////////////////////////////////////////////////////////////////

void draw_smartbomb(SDL_Surface *screen, Game *g, Missile *m)
{
  Uint32 now;
  int direction_x, direction_y;
  int i;
  SDL_Rect r;
  Blast *b;
  double fx = 0.0, fy= 0.0; /* forces acting on smart bomb */
  double bx = 0.0, by= 0.0; /* forces acting on smart bomb from bases */
  double x0, y0;
  double multiplier;
  double best_x0 = 0.0, best_y0 = 0.0;
  double best_dist, current_dist;
  Blast *closest_blast;
 
  if (m->detonated) return;

  now = get_game_time(g);
  
  if ( now < m->created_at ) return;

  //printf("\n");
  
  r.x = RND(m->path->x) - SPRITE[I_SMARTMASK]->w/2;
  r.y = RND(m->path->y) - SPRITE[I_SMARTMASK]->h/2;
  r.w = SPRITE[I_SMARTMASK]->w;
  r.h = SPRITE[I_SMARTMASK]->h;
  //printf("(%03i,%03i) %03ix%03i\n", r.x, r.y, r.w, r.h);
  SDL_BlitSurface( SPRITE[I_SMARTMASK], NULL, screen, &r );
  UpdateRect(&r);
  
  m->end_at = m->created_at;
  m->created_at = now;
  closest_blast = NULL;
  best_dist = SQR(SCREEN_W+1) + SQR(SCREEN_H+1); /* bigger than any number
                                                    we'll calculate */
  
  /* find the closest blast */
  for ( b = g->currentwave.blast; b != NULL; b = b->next )
  {
    if ( b->radius < 0 ) break;
     
    x0 = m->x - b->x;
    y0 = m->y - b->y;

    current_dist = SQR(x0) + SQR(y0) - SQR(b->radius);

    if ( current_dist < best_dist )
    {
      closest_blast = b;
      best_x0 = x0;
      best_y0 = y0;
      best_dist = current_dist;
    }
  }

  /* calculate force if close enough to worry about */
  if ( best_dist < SQR(MISSILE_SMART_R_INFLUENCE) )
  {
    /* if blast is getting smaller then its less worry */
    multiplier = (m->created_at > closest_blast->max_at) ? 0.5 : 1;

    /* which side is the blast on?  Go more that way. */
    direction_x = (best_x0 > 0) ? 1 : -1;
    direction_y = (best_y0 > 0) ? 1 : -1;
 
    fx = multiplier * (direction_x * MISSILE_SMART_R_INFLUENCE - best_x0); 
    fy = multiplier * (direction_y * MISSILE_SMART_R_INFLUENCE - best_y0);

    /* give a push to get out of the way */
    fx = fx + direction_x * ABS(fy)/20;
  }
 
  /* add attraction to the bases */
  i = get_closest_sam(g, RND(m->x) );
  x0 = m->x - g->base[i].location;
  y0 = m->y - SCREEN_CITY_BOTTOM_AT;
  if ( SQR(x0) + SQR(y0) < SQR(MISSILE_SMART_R_INFLUENCE))
  {
    direction_x = (x0 > 0) ? -1 : 1;
    //direction_y = (y0 > 0) ? -1 : 1; /* always false */
 
    bx = direction_x * MISSILE_SMART_R_INFLUENCE - x0; 
    by = MISSILE_SMART_R_INFLUENCE - y0;

    fx += bx;
    fy += by;
  }

  /* normalise to [-1..1] (actually potentially as high as 2.2 if you're
   *  chasing the buggers into your city)  */
  fx = fx / MISSILE_SMART_R_INFLUENCE;
  fy = fy / MISSILE_SMART_R_INFLUENCE;


  m->vx = MISSILE_SMART_SHIFT_X(fx) * m->velocity;
  m->vy = MISSILE_SMART_SHIFT_Y(fy) * m->velocity;
  /*
  printf("time: now = %i, last = %i, elapsed = %i\n",
         m->created_at, m->end_at, m->created_at - m->end_at );
  printf("fx = %+2.4f,  fy = %+2.4f\n", fx, fy );
  printf("vx = %2.2f, vy = %2.2f\n", m->vx, m->vy);
  printf("was at (%03.3f,%03.3f)\n", m->x, m->y);
  */
  
  m->x += m->vx * (m->created_at - m->end_at) / 1000.0;
  m->y += m->vy * (m->created_at - m->end_at) / 1000.0;

  //printf("now at (%03.3f,%03.3f)\n", m->x, m->y);

  if (m->y >= SCREEN_CITY_BOTTOM_AT) m->detonated = 1;

  if ( m->x < 0 ) m->x = 0;
  else if ( m->x > SCREEN_W ) m->x = SCREEN_W;
  if ( m->y < 0 ) m->x = 0;
  else if ( m->y > SCREEN_H ) m->x = SCREEN_H;
  
  //printf("corrected to (%03i,%03i)\n", RND(m->x), RND(m->y));

  m->path->x = RND(m->x);
  m->path->y = RND(m->y);
  
  r.x = RND(m->path->x) - SPRITE[I_SMART]->w/2;
  r.y = RND(m->path->y) - SPRITE[I_SMART]->h/2;
  r.w = SPRITE[I_SMART]->w;
  r.h = SPRITE[I_SMART]->h;

  //printf("(%03i,%03i) %03ix%03i\n", r.x, r.y, r.w, r.h);
  SDL_BlitSurface( SPRITE[I_SMART], NULL, screen, &r );
  UpdateRect(&r);
  
}

///////////////////////////////////////////////////////////////////////////

void zap_smartbomb(SDL_Surface *screen, Missile *m)
{
  SDL_Rect r;
  
  r.x = RND(m->path->x) - SPRITE[I_SMARTMASK]->w/2;
  r.y = RND(m->path->y) - SPRITE[I_SMARTMASK]->h/2;
  r.w = SPRITE[I_SMARTMASK]->w;
  r.h = SPRITE[I_SMARTMASK]->h;
  //printf("(%03i,%03i) %03ix%03i\n", r.x, r.y, r.w, r.h);
  SDL_BlitSurface( SPRITE[I_SMARTMASK], NULL, screen, &r );
  UpdateRect(&r);
}

///////////////////////////////////////////////////////////////////////////

void draw_detonation(SDL_Surface *screen, Game *g, Blast *blast)
{
  //SDL_Rect r;
  SDL_Rect c;
  SDL_Surface *image = NULL;
  int old_radius;
  
  if ( get_game_time(g) < blast->created_at )
  {
    // printf("too early to detonate\n");
    return;
  }
  
  old_radius = blast->radius;
    
  if ( get_game_time(g) < blast->max_at ) /* explosion is expanding */
  {
    blast->radius = RND((get_game_time(g) - blast->created_at) / 1000.0 *
                        blast->velocity);
  }
  else /* explosion is retreating */
  {
    blast->radius = RND(blast->maxradius +
                        ((double)blast->max_at - get_game_time(g)) / 1000.0 *
                        blast->velocity);
  }

  image = diff_blast( screen, old_radius, blast->radius );

  if (image == NULL)
  {
    printf("dkfjhaskfhasgfhasd\n");
    exit(-1);
  }
 
  c.x = blast->x - image->w/2;
  c.y = blast->y - image->h/2;
  c.w = image->w;
  c.h = image->h;
    
  SDL_BlitSurface( image, NULL, screen, &c );
  UpdateRect(&c);
    
  SDL_FreeSurface(image);
 
}

///////////////////////////////////////////////////////////////////////////

/* create a ring that is the difference between a blast at 2 radii */
SDL_Surface *diff_blast( SDL_Surface *screen, int old_radius, int new_radius )
{
  SDL_Surface *the_circle;
  Uint32 bg, add, del;
  int i,j;
  int x0, y0;
  int circle_radius;
  
  if (old_radius < 0) old_radius = 0;
  if (new_radius < 0) new_radius = 0;

  circle_radius = MAX(old_radius,new_radius);
  
  the_circle = SDL_CreateRGBSurface( SDL_HWSURFACE | SDL_SRCCOLORKEY,
                                     circle_radius * 2 + 1,
                                     circle_radius * 2 + 1,
                                     screen->format->BitsPerPixel,
                                     screen->format->Rmask,
                                     screen->format->Gmask,
                                     screen->format->Bmask,
                                     screen->format->Amask );

  if (the_circle->format->BitsPerPixel == 8)
    SDL_SetColors( the_circle, G_COLOUR, 0, G_NUMBER_COLOURS );

  if ( the_circle == NULL )
  {
    fprintf(stderr, "Not enough memory to allocate circle in diff_blast()\n");
    return NULL;
  }
  
  bg  = SDL_MapRGB(the_circle->format, G_ALPHA);
  add = SDL_MapRGB(the_circle->format, G_YELLOW);
  del = SDL_MapRGB(the_circle->format, G_BLACK);

  if ( SDL_MUSTLOCK(the_circle) && (SDL_LockSurface(the_circle) < 0) )
  {
    SDL_FreeSurface(the_circle);
    fprintf(stderr, "*** couldn't get lock to create circle\n");
    return NULL;
  }

  for ( j=0; j< the_circle->h; j++ )
  {
    y0 = j - circle_radius;
    y0 = SQR(y0);
    for ( i=0; i< the_circle->w; i++ )
    {
      x0 = i - circle_radius;
      x0 = SQR(x0);
      if ( (y0 + x0 <= SQR(new_radius)) && (new_radius != 0))
      {
        DRAWPIXEL( the_circle, i, j, add );
      }
      else if (y0 + x0 <= SQR(old_radius))
      {
        DRAWPIXEL( the_circle, i, j, del );
      }
      else
      {
        DRAWPIXEL( the_circle, i, j, bg );
      }
    }
  }
  
  if ( SDL_MUSTLOCK(the_circle) )  SDL_UnlockSurface(the_circle);

  SDL_SetColorKey( the_circle, SDL_SRCCOLORKEY, bg );
    
  return the_circle;
}

///////////////////////////////////////////////////////////////////////////

void update_scoreboard( SDL_Surface *screen, Game *g, int force )
{
  SDL_Rect r, b, s;
  Uint32 bg;
  int number, length, digit;
  int i;

  b.x = 0;
  b.y = STATUS_AT_Y - 4;
  b.w = SCREEN_W;
  b.h = STATUS_SIZE + 4;

  bg = SDL_MapRGB(screen->format, G_BLACK);
  SDL_FillRect(screen, &b, bg);

  r.y = STATUS_AT_Y;
  r.w = NUMBER_COLOURED_W;
  r.h = NUMBER_COLOURED_H;

  s.w = NUMBER_COLOURED_W;
  s.h = NUMBER_COLOURED_H;

  
  SDL_BlitSurface( SPRITE[I_STATUS_WAVE], NULL,
                   screen, &IMAGE_LOCATION[I_STATUS_WAVE] ); 
 
  number = g->wave_number + 1;
  if ( number <= 0)
    length = 2;
  else
  {
    length = (int)log10(number) + 1; /* number of digits in the wave number */
    length = MAX(length, 2);         /* %02i */
  }
  
  r.x = IMAGE_LOCATION[I_STATUS_WAVE].x + SPRITE[I_STATUS_WAVE]->w
    + length * NUMBER_COLOURED_W + STATUS_SPACE;

  s.y = BLUE * NUMBER_COLOURED_H; 

  for ( i=length-1; i>=0; i-- )
  {
    digit = number % 10;
    number = number / 10;

    r.x -= NUMBER_COLOURED_W;
    s.x = digit * NUMBER_COLOURED_W;
  
    SDL_BlitSurface( SPRITE[I_COLOUR_NUMBER], &s, screen, &r ); 
    UpdateRect(&r);
  }

  
  SDL_BlitSurface( SPRITE[I_STATUS_SCORE], NULL,
                   screen, &IMAGE_LOCATION[I_STATUS_SCORE] ); 

  number = g->score;
  if ( number <= 0)
    length = 7;
  else
  {
    length = (int)log10(number) + 1; /* number of digits in the wave number */
    length = MAX(length, 7);         /* %07i */
  }
  
  r.x = IMAGE_LOCATION[I_STATUS_SCORE].x + SPRITE[I_STATUS_SCORE]->w
    + length * NUMBER_COLOURED_W + STATUS_SPACE;

  s.y = g->scorecolor * NUMBER_COLOURED_H; 

  for ( i=length-1; i>=0; i-- )
  {
    digit = number % 10;
    number = number / 10;

    r.x -= NUMBER_COLOURED_W;
    s.x = digit * NUMBER_COLOURED_W;
  
    SDL_BlitSurface( SPRITE[I_COLOUR_NUMBER], &s, screen, &r ); 
    UpdateRect(&r);
  }

  
  SDL_BlitSurface( SPRITE[I_STATUS_HIGH], NULL,
                   screen, &IMAGE_LOCATION[I_STATUS_HIGH] ); 


  number = g->highscore;
  if ( number <= 0)
    length = 7;
  else
  {
    length = (int)log10(number) + 1; /* number of digits in the wave number */
    length = MAX(length, 7);         /* %07i */
  }
  
  r.x = IMAGE_LOCATION[I_STATUS_HIGH].x + SPRITE[I_STATUS_HIGH]->w
    + length * NUMBER_COLOURED_W + STATUS_SPACE;

  s.y = GREEN * NUMBER_COLOURED_H; 

  for ( i=length-1; i>=0; i-- )
  {
    digit = number % 10;
    number = number / 10;

    r.x -= NUMBER_COLOURED_W;
    s.x = digit * NUMBER_COLOURED_W;
  
    SDL_BlitSurface( SPRITE[I_COLOUR_NUMBER], &s, screen, &r ); 
    UpdateRect(&r);
  }


  if (force) /* update right now */
    SDL_UpdateRects( screen, 1, &b ); 
  else  /* update when it can be bothered */
    UpdateRect(&b);
}

///////////////////////////////////////////////////////////////////////////

/* Bresenham's line drawing algorithm */
Point* lineBresenham(int *size, int x0, int y0, int x1, int y1)
{
  Point *the_line;
  //  int i;
  
  int point_at = 0;
  int dy = y1 - y0; 
  int dx = x1 - x0;
  int stepx, stepy;

  *size = MAX(ABS(dy),ABS(dx)) + 1;
  the_line = (Point *)malloc( *size * sizeof(Point) ); 

  if (the_line == NULL)
  {
    printf("couldn't malloc %i bytes in lineBresenham\n", *size*sizeof(Point));
  }
  
  //printf("made line with %i elements\n", *size );
  //printf("(%03i,%03i) - (%03i,%03i)\n", x0, y0, x1, y1 );
  
  if (dy < 0) { dy = -dy;  stepy = -1; } else { stepy = 1; }
  if (dx < 0) { dx = -dx;  stepx = -1; } else { stepx = 1; }
  dy <<= 1;                           
  dx <<= 1;                           
                                      
  the_line[point_at].x = x0;
  the_line[point_at].y = y0;
  point_at++;
  if (dx > dy)
  {                      
    int fraction = dy - (dx >> 1);    
    while (x0 != x1)
    {                
      if (fraction >= 0)
      {            
        y0 += stepy;                  
        fraction -= dx;               
      }                               
      x0 += stepx;                    
      fraction += dy;                 
      the_line[point_at].x = x0;
      the_line[point_at].y = y0;
      point_at++;
    }
  }
  else
  {
    int fraction = dx - (dy >> 1);
    while (y0 != y1)
    {
      if (fraction >= 0)
      {
        x0 += stepx;
        fraction -= dy;
      }
      y0 += stepy;
      fraction += dx;
      the_line[point_at].x = x0;
      the_line[point_at].y = y0;
      point_at++;
    }
  }

  /*  
  if ( point_at != *size )
    fprintf(stderr, "size fubar in lineBresenham\n");

  printf("made line starting at (%03i,%03i) ending (%03i,%03i) length %i\n",
         the_line[0].x, the_line[0].y,
         the_line[point_at-1].x, the_line[point_at-1].y,
         *size );
  */
  //for (i=0; i<*size; i++)
  //  printf("%3i : (%03i,%03i)\n", i, the_line[i].x, the_line[i].y );
  
  return the_line;
}

///////////////////////////////////////////////////////////////////////////

void UpdateRect(SDL_Rect *r)
{
  //printf("adding %ith rect to be updated (%03i,%03i,%03i,%03i)\n",
  //       LAST_RECT, r->x, r->y, r->w, r->h);

  if ( LAST_RECT == -1 ) return; /* already gunna redraw the whole screen */
  
  if ( LAST_RECT == MAX_ITEMS_TO_UPDATE ) /* too many, do the whole screen */
  {
    RECTS_TO_UPDATE[0].x = 0;
    RECTS_TO_UPDATE[0].y = 0;
    RECTS_TO_UPDATE[0].w = SCREEN_W;
    RECTS_TO_UPDATE[0].h = SCREEN_H;

    LAST_RECT = -1;
  }
  else
  {
    RECTS_TO_UPDATE[LAST_RECT].x = r->x;
    RECTS_TO_UPDATE[LAST_RECT].y = r->y;
    RECTS_TO_UPDATE[LAST_RECT].w = MIN(SCREEN_W - r->x, r->w);
    RECTS_TO_UPDATE[LAST_RECT].h = MIN(SCREEN_H - r->y, r->h);

    LAST_RECT++;
  }
}


///////////////////////////////////////////////////////////////////////////

/* test to see if a missile has hit an explosion or a building */
void collision_detection( Missile *m, Game *g )
{
  Blast *t;
  int x0, x1, y0;
  int i, j;

  if ( get_game_time(g) < m->created_at ) /* missile doesn't exist yet */
    return;
   
  /* test against explosions */
  for ( t = g->currentwave.blast; t != NULL; t = t->next )
  {
    if ( t->radius < 0 ) break;
    if ( (m->at < 0) || (m->at >= m->path_length) ) break;

    x0 = m->path[m->at].x - t->x;
    y0 = m->path[m->at].y - t->y;
    
    if ( SQR(x0) + SQR(y0) <= SQR(t->radius) )
    {
      create_blast_from_missile(&(g->currentwave.blast), m, get_game_time(g));
      switch (m->type)
      {
        case REGULAR:
          add_to_score( g, POINTS_REGULAR_MISSILE );
          break;
        case MIRV:
          add_to_score( g, POINTS_MIRV_MISSILE );
          break;
        case SMART:
          add_to_score( g, POINTS_SMART_MISSILE );
          break;
        default: /* no points for anything else */
          break;
      }
    }
  }

  /* test against cities */
  for ( i=0; i<6; i++ )
  {
    if ( g->city[i].alive )
      if ( m->path[m->at].y >= SCREEN_CITY_HOTSPOT_AT )
      {
        x0 = g->city[i].location - SCREEN_CITY_WIDTH / 2;
        x1 = g->city[i].location + SCREEN_CITY_WIDTH / 2;
      
        if ( (m->path[m->at].x > x0) && (m->path[m->at].x < x1) )
        {
          create_blast_from_missile(&(g->currentwave.blast), m,
                                    get_game_time(g));
          g->city[i].alive = 0;
          //printf("city %i went bye bye\n", i);
          
          for ( j=0; j<5; j++ )
          {
            x0 = (random() % SCREEN_CITY_WIDTH) - SCREEN_CITY_WIDTH / 2;
            y0 = (random() % SCREEN_CITY_WIDTH / 2) - SCREEN_CITY_WIDTH / 2; 
            create_blast_at( &(g->currentwave.blast),
                             get_game_time(g) + (random() % 1000),
                             x0 + g->city[i].location,
                             y0 + SCREEN_CITY_HOTSPOT_AT );
          }
        }
      }
  }
  
  /* test against bases */
  for ( i=0; i<3; i++ )
  {
    if ( g->base[i].alive )
      if ( m->path[m->at].y >= SCREEN_CITY_HOTSPOT_AT )
      {
        x0 = g->base[i].location - SCREEN_CITY_WIDTH / 2;
        x1 = g->base[i].location + SCREEN_CITY_WIDTH / 2;
      
        if ( (m->path[m->at].x > x0) && (m->path[m->at].x < x1) )
        {
          create_blast_from_missile(&(g->currentwave.blast), m,
                                    get_game_time(g));
          g->base[i].alive = 0;
          g->base[i].num_sams = 0;
          //printf("base %i went bye bye\n", i);
          
          for ( j=0; j<5; j++ )
          {
            x0 = (random() % SCREEN_CITY_WIDTH) - SCREEN_CITY_WIDTH / 2;
            y0 = (random() % SCREEN_CITY_WIDTH / 2) - SCREEN_CITY_WIDTH / 2; 
            create_blast_at( &(g->currentwave.blast),
                             get_game_time(g) + (random() % 1000),
                             x0 + g->base[i].location,
                             y0 + SCREEN_CITY_HOTSPOT_AT );
          }
        }
      }
  }
  
}

///////////////////////////////////////////////////////////////////////////

int get_closest_sam( Game *g, int x )
{
  int sum = (g->base[0].num_sams?1:0) + (g->base[1].num_sams?2:0) + (g->base[2].num_sams?4:0);

  switch (sum)
  {
    case 0: /* you're hurting */
      return 0;
      break;
    case 1: case 2: case 4:
      return sum / 2;
      break;
    case 3:
      if ( x < SAMSITE_ZONE_1 ) return 0; else return 1;
      break;
    case 5:
      if ( x < SAMSITE_ZONE_2 ) return 0; else return 2;
      break;
    case 6:
      if ( x < SAMSITE_ZONE_3 ) return 1; else return 2;
      break;
    case 7:
      if ( x < SAMSITE_ZONE_1 ) return 0;
      else if ( x > SAMSITE_ZONE_3 ) return 2;
      else return 1;
      break;
  }

  return 0;
}

///////////////////////////////////////////////////////////////////////////

/* not close enough in properties to bother merging with fire_missile */
void fire_sam(Missile **ml, SamSite *site, Uint32 t, Uint16 at_x, Uint16 at_y)
{
  Missile *s;
  double ratio;
  
  if ( site->num_sams == 0 )
  {
    // printf("Outta ammo\n");
    return;
  }
  
  site->num_sams--;
  
  s = (Missile *)malloc( sizeof(Missile) );
  
  if (s == NULL)
  {
    printf("couldn't malloc %i bytes in fire_sam\n", sizeof(Missile));
  }

  s->type = SAM;
  
  s->detonated = 0;
  s->at = 0;
  s->created_at = t;
 
  s->path = lineBresenham( &(s->path_length),
                           site->location, SAMSITE_SHOOT_FROM, at_x, at_y );

  s->velocity = SAM_VELOCITY;
  ratio = (site->location - (double)at_x) / (SAMSITE_SHOOT_FROM -(double)at_y);
  s->vx = sqrt( SQR(s->velocity) / (SQR(ratio) + 1.0) );
  s->vy = sqrt( SQR(s->velocity) - SQR(s->vx) );
 
  s->end_at = s->created_at + RND((s->path_length -1) / s->velocity * 1000.0);
 
  /*
  printf("Path length = %i\n", s->path_length);
  printf("sam heading to (%03i,%03i) at vx=%2.2f vy=%2.2f => v=%2.2f\n",
         target_x, target_y, s->vx, s->vy, sqrt(SQR(s->vx) + SQR(s->vy)));
  */
  
  s->next = *ml;
  *ml = s;
}

///////////////////////////////////////////////////////////////////////////

void fire_missile_from_xy(Missile **ml, int wave, Uint32 time,
                          MissileFlavour flavour, int x0, int y0)
{
  Missile *m;
  int x1, y1;
  double ratio;

  m = (Missile *)malloc( sizeof(Missile) );
 
  if (m == NULL)
  {
    printf("couldn't malloc %i bytes in fire_missile\n", sizeof(Missile));
    exit(-1);
  }
  
  x1 = random() % SCREEN_W;
  y1 = SCREEN_CITY_BOTTOM_AT;
 
  m->type = flavour;
  m->detonated = 0;
  m->created_at = time;
  m->at = 0;
  m->has_split = 0;


  m->velocity = MISSILE_VELOCITY(m->type,wave);
  if ( m->type != SMART )
  {
    m->path = lineBresenham( &(m->path_length), x0, y0, x1, y1 );
    ratio = ( x0 - (double)x1 ) / ( y0 - (double)y1 );
    m->vx = sqrt( SQR(m->velocity) / (SQR(ratio) + 1.0) );
    m->vy = sqrt( SQR(m->velocity) - SQR(m->vx) );
    m->end_at = m->created_at + RND((m->path_length -1) / m->velocity * 1000.0);
  }
  else
  {
    m->path = (Point *)malloc( sizeof(Point) );
    m->x = m->path->x = x0;
    m->y = m->path->y = y0;
    m->path_length = 1;
    m->end_at = m->created_at; /* use to track last updated times */
  }

  if  ( m->type == MIRV )  /* some fakery */
    m->path_length = random() % MISSILE_MIRV_MIN_HEIGHT;

  
  m->next = *ml;
  *ml = m;
}

///////////////////////////////////////////////////////////////////////////

void trigger_mirv( Missile **ml, int wave, Uint32 time, Missile *m )
{
  int i, num;
  
  m->has_split = 1;
  num = random() % MISSILE_NUMBER_MIRVS(wave) + MISSILE_MIN_MIRVS;
  for ( i=0; i<num; i++ )
    fire_missile_from_xy( ml, wave, time, REGULAR,
                          m->path[m->at].x, m->path[m->at].y );   
}

///////////////////////////////////////////////////////////////////////////

void create_blast_from_sam( Blast **blast_list, Missile *s, Uint32 time )
{
  Blast *b;

  b = (Blast *)malloc( sizeof(Blast) );

  b->created_at = time;
  b->x          = s->path[s->path_length-1].x;
  b->y          = s->path[s->path_length-1].y;
  b->velocity   = SAM_EXPLOSION_VELOCITY;
  b->radius     = 0;
  b->maxradius  = SAM_EXPLOSION_RADIUS;
  b->next       = NULL;
  b->max_at     = b->created_at + RND((b->maxradius / b->velocity) * 1000);
  b->end_at     = b->created_at + RND((b->maxradius / b->velocity) * 1000 * 2);
    
  b->next = *blast_list;
  *blast_list = b;
}

///////////////////////////////////////////////////////////////////////////

void create_blast_from_missile( Blast **blast_list, Missile *m, Uint32 time )
{
  Blast *b;

  b = (Blast *)malloc( sizeof(Blast) );

  b->created_at = time;
  b->x          = m->path[m->at].x;
  b->y          = m->path[m->at].y;
  b->velocity   = MISSILE_EXPLOSION_VELOCITY;
  b->radius     = 0;
  b->maxradius  = MISSILE_EXPLOSION_RADIUS;
  b->next       = NULL;
  b->max_at     = b->created_at + RND((b->maxradius / b->velocity) * 1000);
  b->end_at     = b->created_at + RND((b->maxradius / b->velocity) * 1000 * 2);

  m->detonated  = 1;
  m->has_split  = 1;
  
  b->next = *blast_list;
  *blast_list = b;
}

///////////////////////////////////////////////////////////////////////////

void create_blast_at( Blast **blast_list, int t, int x, int y )
{
  Blast *b;

  b = (Blast *)malloc( sizeof(Blast) );

  b->created_at = t;
  b->x          = x;
  b->y          = y;
  b->velocity   = MISC_EXPLOSION_VELOCITY;
  b->radius     = 0;
  b->maxradius  = MISC_EXPLOSION_RADIUS;
  b->next       = NULL;
  b->max_at     = b->created_at + RND((b->maxradius / b->velocity) * 1000);
  b->end_at     = b->created_at + RND((b->maxradius / b->velocity) * 1000 * 2);
  
  b->next = *blast_list;
  *blast_list = b;
}

///////////////////////////////////////////////////////////////////////////

void delete_used_missiles( Missile **missile_list )
{
  Missile *check, *tmp;

  if (*missile_list == NULL) return;
  
  /* special case head */
  check=*missile_list;
  while (check->detonated) 
  {
    *missile_list = check->next;
    free(check);
    check = *missile_list;
    if (check == NULL) return;
  }

  /* check rest */
  check=*missile_list;
  while ( check->next != NULL )
  {
    if ( check->next->detonated )
    {
      tmp = check->next->next;
      free(check->next);
      check->next = tmp;
    }
    else
      check = check->next;
  }
}

///////////////////////////////////////////////////////////////////////////

void delete_finished_explosions( Blast **blast_list, Uint32 time )
{
  Blast *check, *tmp;
  Uint32 thetime;
  
  if (*blast_list == NULL) return;

  thetime = time - 1000/FPS; /* buy an extra frame */
  
  /* special case head */
  check=*blast_list;
  while (check->end_at < thetime) 
  {
    *blast_list = check->next;
    free(check);
    check = *blast_list;
    if (check == NULL) return;
  }

  /* check rest */
  while ( check->next != NULL )
  {
    if ( check->next->end_at < thetime )
    {
      tmp = check->next->next;
      free(check->next);
      check->next = tmp;
    }
    else
      check = check->next;
  }
}

///////////////////////////////////////////////////////////////////////////

void load_images( Game *g )
{
  Uint32 alpha;
  char filename[RCFILE_MAX_LINE_SIZE + 30];
  SDL_Surface *tmp_image;
  int i;
  
  for ( i=0; i<NUMBER_SPRITES; i++ )
  {
    sprintf(filename, "%s/%s", g->data_dir, SPRITE_FILENAME[i] );
    if ( (SPRITE[i] = IMG_Load(filename)) == NULL )
    {
      printf("\nERROR: reading game data '%s'\n", SPRITE_FILENAME[i]);
      perror("Trying to open the file reports");
      printf("The error is: %s\n\n", SDL_GetError());
      printf("If you need to set the data directory, ");
      printf("run \"missile --set-datadir=<datadir>\".\n");
      printf("\nRun \"missile -h\" for command line options\n\n");
      exit(-1);       
    }
    //   SDL_SetColors( SPRITE[i], G_COLOUR, 0, G_NUMBER_COLOURS );
  }

  /* the formats the same so we can reuse this one.
   * we use black here because we have antialiased images */
  alpha = SDL_MapRGB(SPRITE[I_CITY]->format, G_BLACK);
  SDL_SetColorKey(SPRITE[I_CITY], SDL_SRCCOLORKEY, alpha);
  SDL_SetColorKey(SPRITE[I_BASE], SDL_SRCCOLORKEY, alpha);
  SDL_SetColorKey(SPRITE[I_PAUSED], SDL_SRCCOLORKEY, alpha);
  //SDL_SetColorKey(IMAGE.city_dead, SDL_SRCCOLORKEY, alpha);
  //SDL_SetColorKey(IMAGE.base_dead, SDL_SRCCOLORKEY, alpha);
  
  SDL_SetColors( SPRITE[I_SMART],     G_COLOUR, 0, G_NUMBER_COLOURS );
  SDL_SetColors( SPRITE[I_SMARTMASK], G_COLOUR, 0, G_NUMBER_COLOURS );
  /* and just to be different: */
  alpha = SDL_MapRGB(SPRITE[I_SMART]->format, G_ALPHA);
  SDL_SetColorKey(SPRITE[I_SMART],     SDL_SRCCOLORKEY, alpha);
  SDL_SetColorKey(SPRITE[I_SMARTMASK], SDL_SRCCOLORKEY, alpha);

	/* Set transparent pixel as the pixel at (0,0) 
	if ( SPRITE[I_SMART]->format->palette ) {
		SDL_SetColorKey(SPRITE[I_SMART], (SDL_SRCCOLORKEY|SDL_RLEACCEL),
						*(Uint8 *)SPRITE[I_SMART]->pixels);
	}
	if ( SPRITE[I_SMARTMASK]->format->palette ) {
		SDL_SetColorKey(SPRITE[I_SMARTMASK], (SDL_SRCCOLORKEY|SDL_RLEACCEL),
						*(Uint8 *)SPRITE[I_SMARTMASK]->pixels);
	}
  */
  
  /* change bitmap colour depths to the display depth (speeds up blits)
   *  make sure it is done *after* all SDL_SetColorKey() calls */
  for ( i=0; i<NUMBER_SPRITES-2; i++ ) // do all but the smartbombs
  {
    tmp_image = SDL_DisplayFormat( SPRITE[i] );
    SDL_FreeSurface( SPRITE[i] ); 
    SPRITE[i] = tmp_image;    
    //  SDL_SetColors( SPRITE[i], G_COLOUR, 0, G_NUMBER_COLOURS );
  }
  
 
  /* positions of static images */
  set_image_position( I_TITLE_MISSILECOMMAND,
                      SCREEN_W / 2 - SPRITE[I_TITLE_MISSILECOMMAND]->w / 2,
                      SCREEN_H / 2 - SPRITE[I_TITLE_MISSILECOMMAND]->h );

  set_image_position( I_TITLE_ENTERTOSTART, 
                      SCREEN_W / 2 - SPRITE[I_TITLE_ENTERTOSTART]->w / 2,
                      SCREEN_H / 2 + SPRITE[I_TITLE_ENTERTOSTART]->h );
 
  set_image_position( I_GAMEOVER,
                      SCREEN_W / 2 - SPRITE[I_GAMEOVER]->w / 2,
                      SCREEN_H / 2 - SPRITE[I_GAMEOVER]->h / 2 );
 
  set_image_position( I_PAUSED,
                      SCREEN_W / 2 - SPRITE[I_PAUSED]->w / 2,
                      SCREEN_H / 2 - SPRITE[I_PAUSED]->h / 2 );
  
  set_image_position( I_WAVE,
                      SCREEN_W / 2 - SPRITE[I_WAVE]->w / 2 - NUMBER_WAVE_W,
                      SCREEN_H / 2 - SPRITE[I_WAVE]->h / 2 );

  set_image_position( I_STATUS_WAVE,  STATUS_WAVE_AT_X,      STATUS_AT_Y );
  set_image_position( I_STATUS_SCORE, STATUS_SCORE_AT_X,     STATUS_AT_Y );
  set_image_position( I_STATUS_HIGH,  STATUS_HIGHSCORE_AT_X, STATUS_AT_Y );
}

///////////////////////////////////////////////////////////////////////////

void set_image_position( int image, int x, int y )
{
  IMAGE_LOCATION[image].x = x;
  IMAGE_LOCATION[image].y = y;
  IMAGE_LOCATION[image].w = SPRITE[image]->w;
  IMAGE_LOCATION[image].h = SPRITE[image]->h;
}

///////////////////////////////////////////////////////////////////////////

double bias( double b, double t )
{
  if (t <= 0.0) return 0; else if ( t >= 1.0 ) return 1.0;
  
  return pow( t, (log(b)/log(0.5)) );
}

///////////////////////////////////////////////////////////////////////////

/* calculate how many ms to wait to fire the next incoming missile */
Uint32 gap_for_next_missile( int wave )
{
  double min, max, timing;

  min =  (1.0 - bias(0.92, (double)wave/50.0)) * 1500 + 50.0;
  max = min + (1.0 - bias(0.7, (double)wave/100.0)) * 500;
  
  timing = min +  ( random() % (int)(1 + max - min) );

  return (Uint32)timing;
}

///////////////////////////////////////////////////////////////////////////

void initwave_data( Game *g , int wavenumber )
{
  long int phasestart;
  long int nextphasestart = 0;
  int i, j, num;
  Uint32 when;
  Wave *l = &(g->currentwave);
  Uint32 last;
  int chance_smart, chance_mirv, what_we_got;
  MissileFlavour flavour;
  Missile *m;

  /* add any new cities */
  num = random();
  for (i=0; g->reserve_cities && (i<6); i++)
  {
    if ( !g->city[(i+num)%6].alive )
    {
      g->city[(i+num)%6].alive = 1;
      g->reserve_cities--;
    }
  }
  
  /* add any new bases */
  num = random();
  for (i=0; g->reserve_bases && (i<3); i++)
  {
    if ( !g->base[(i+num)%3].alive )
    {
      g->base[(i+num)%3].alive = 1;
      g->reserve_bases--;
    }
  }
  
  
  l->starttime     = get_game_time(g);
  l->num_phases    = PHASE_NUMBER_PHASES(wavenumber);
  //printf("%i phases in wave %i\n", l->num_phases, wavenumber);

  l->num_missiles  = (int *)malloc( sizeof(int) * l->num_phases );
  l->missile = NULL;
  l->totalmissiles = 0;

  phasestart = l->starttime;
  for (j=0; j<l->num_phases; j++)
  {
    l->num_missiles[j] = PHASE_NUMBER_MISSILES(wavenumber);
    //printf("%i missiles in phase %i\n", l->num_missiles[j], j);

    when = 0;

    last = phasestart;
    // printf("phase %i begins at %lu\n", j, phasestart);
    for (i=l->totalmissiles; i<l->num_missiles[j]+l->totalmissiles; i++)
    {
      when = gap_for_next_missile( wavenumber );
      //printf("when = %6i\n", when );

      chance_mirv  = MISSILE_CHANCE_MIRV(wavenumber,j);
      chance_smart = MISSILE_CHANCE_SMART(wavenumber,j) + chance_mirv;
      
      what_we_got = random() % 100;
      if ( what_we_got < chance_mirv )
        flavour = MIRV;
      else if ( what_we_got < chance_smart )
        flavour = SMART;
      else
        flavour = REGULAR;
      
      fire_missile( &(l->missile), wavenumber, when + last, flavour );
      
      //printf("missile %i fires at %lu\n", i, l->missile->created_at);
      nextphasestart = MAX( nextphasestart, l->missile->created_at );
      last += when;
    }
    phasestart = nextphasestart + PHASE_INTERVAL(wavenumber);
    l->totalmissiles += l->num_missiles[j];
  }

  l->endtime = phasestart + PHASE_WAIT_AT_END;
  
  //printf("%i missiles total.\n", l->totalmissiles);

  l->sam = NULL;
  l->blast = NULL;

    
  for (i=0; i<3; i++)
  {
    if ( g->base[i].alive)
      g->base[i].num_sams = SAMSITE_BASE_NUM_SAMS;
  }

  /* see if we can catch the out of bounds missile problem */
  for ( m = l->missile; m != NULL; m = m->next )
  {
    if ( (m->path->x < 0) || (SCREEN_W <= m->path->x) ||
         (m->path[m->path_length-1].x < 0) ||
         (SCREEN_W <= m->path[m->path_length-1].x) )
    {
      printf("Generated a bad missile:\n");
      printf("Type = %i\n", m->type);
      printf("created at %i\n", m->created_at);
      printf("at=0 is %03i, at=max is %03i\n",
             m->path[0].x, m->path[m->path_length-1].x);
      m->detonated = 1;
    }
  }
  
}

///////////////////////////////////////////////////////////////////////////

void freewave_data( Game *g )
{
  Missile *m;
  Blast *b;
  Wave *l = &g->currentwave; 
  
  free(l->num_missiles);

  if (l->missile != NULL)
  {
    printf("WARNING: not all missiles fired\n");
    while ( l->missile ) 
    {
      m = l->missile;
      l->missile = l->missile->next;
      free(m->path);
      free(m);    
    }
  }

  if (l->sam != NULL)
  {
    printf("Some sams still enroute\n");
    while ( l->sam ) 
    {
      m = l->sam;
      l->sam = l->sam->next;
      free(m->path);
      free(m);    
    }
  }

  if (l->blast != NULL)
  {
    printf("Some explosions still happening\n");
    while ( l->blast ) 
    {
      b = l->blast;
      l->blast = l->blast->next;
      free(b);    
    }
  }

}

///////////////////////////////////////////////////////////////////////////

void initgame_data( Game *g )
{
  int i,j=0;
  int position[9];

  int incr = (SCREEN_W / 9);
  for (i=incr/2; i<SCREEN_W; i+=incr)
  {
    position[j] = i;
    j++;
  }
  
  for (i=0; i<3; i++)
  {
    g->base[i].alive = 1;
    g->base[i].num_sams = SAMSITE_BASE_NUM_SAMS;
  }
  
  for (i=0; i<6; i++)
  {
    g->city[i].alive = 1;
  }

  g->base[0].location = position[0];
  g->city[0].location = position[1];
  g->city[1].location = position[2];
  g->city[2].location = position[3];
  g->base[1].location = position[4];
  g->city[3].location = position[5];
  g->city[4].location = position[6];
  g->city[5].location = position[7];
  g->base[2].location = position[8];


  g->reserve_cities = 0;
  g->reserve_bases  = 0;
  g->score          = 0;
  g->scorecolor     = RED;
  g->wave_number    = 0;
  g->is_paused      = 0;
  g->pause_time     = 0;
  g->diff_time      = 0;
  g->gameover       = 0;
}

///////////////////////////////////////////////////////////////////////////

void draw_cities( SDL_Surface *screen, Game *g, int force )
{
  int i;
  SDL_Rect r;

  r.y = SCREEN_CITY_AT;
  r.w = SCREEN_CITY_WIDTH;
  r.h = SCREEN_CITY_HEIGHT;
 
  for ( i=0; i<6; i++ )
  {
    r.x = g->city[i].location - SCREEN_CITY_WIDTH / 2;
    if ( g->city[i].alive )
      SDL_BlitSurface( SPRITE[I_CITY] , NULL, screen, &r );
    //else
    //  SDL_BlitSurface( IMAGE.city_dead , NULL, screen, &r );
    if (force)
      SDL_UpdateRects(screen, 1, &r);
    else
      UpdateRect(&r);
  }
  
}

///////////////////////////////////////////////////////////////////////////

void draw_bases( SDL_Surface *screen, Game *g, int force )
{
  int i,j;
  SDL_Rect r, t, s;
  int number, length, digit;

  r.y = SCREEN_CITY_AT;
  r.w = SCREEN_CITY_WIDTH;
  r.h = SCREEN_CITY_HEIGHT;
  
  for ( i=0; i<3; i++ )
  {
    r.x = g->base[i].location - SCREEN_CITY_WIDTH / 2;
    if ( g->base[i].alive )
    {
      SDL_BlitSurface( SPRITE[I_BASE] , NULL, screen, &r );

      number = g->base[i].num_sams;
      if ( number <= 0 )
      {
        length = 2;
      }
      else
      {
        length = (int)log10(number) + 1; /* number of digits */
        length = MAX(length, 2);         /* %02i */
      }
      
      t.x = g->base[i].location + (length * NUMBER_SAM_W / 2);
      t.y = SCREEN_CITY_BOTTOM_AT - NUMBER_SAM_H;
      t.w = NUMBER_SAM_W;
      t.h = NUMBER_SAM_H;

      s.y = 0; 
      s.w = NUMBER_SAM_W;
      s.h = NUMBER_SAM_H;

      for ( j=length-1; j>=0; j-- )
      {
        digit = number % 10;
        number = number / 10;

        t.x -= NUMBER_SAM_W;
        s.x = digit * NUMBER_SAM_W;
  
        SDL_BlitSurface( SPRITE[I_SAM_NUMBER], &s, screen, &t ); 
        UpdateRect(&t);
      }
    }
    /*
      else
      SDL_BlitSurface( IMAGE.base_dead , NULL, screen, &r );
    */
    if (force)
      SDL_UpdateRects(screen, 1, &r);
    else
      UpdateRect(&r);
  }
  
}

///////////////////////////////////////////////////////////////////////////

void show_game_splash_screen( SDL_Surface *screen )
{
  SDL_BlitSurface( SPRITE[I_TITLE_MISSILECOMMAND], NULL,
                   screen, &IMAGE_LOCATION[I_TITLE_MISSILECOMMAND] ); 
  UpdateRect(&IMAGE_LOCATION[I_TITLE_MISSILECOMMAND]);
  
  SDL_BlitSurface( SPRITE[I_TITLE_ENTERTOSTART], NULL,
                   screen, &IMAGE_LOCATION[I_TITLE_ENTERTOSTART] ); 
  UpdateRect(&IMAGE_LOCATION[I_TITLE_ENTERTOSTART]);
}

///////////////////////////////////////////////////////////////////////////

void show_game_over_screen( SDL_Surface *screen )
{
  SDL_Rect b;
  Uint32 bg;
  
  b.x = 0;
  b.y = 0;
  b.w = SCREEN_W;
  b.h = SCREEN_CITY_AT;
  
  bg = SDL_MapRGB(screen->format, G_BLACK);
  SDL_FillRect(screen, &b, bg);
  SDL_UpdateRects(screen, 1, &b);

  SDL_BlitSurface(SPRITE[I_GAMEOVER], NULL, screen,
                  &IMAGE_LOCATION[I_GAMEOVER]); 
  SDL_UpdateRects(screen, 1, &IMAGE_LOCATION[I_GAMEOVER]);

  SDL_Delay(2000);
  SDL_FillRect( screen, &b, SDL_MapRGB(screen->format, G_BLACK) ); 
  SDL_UpdateRects(screen, 1, &b);
}

///////////////////////////////////////////////////////////////////////////

void show_wave_splash_screen( SDL_Surface *screen, int number )
{
  SDL_Rect r, s, b;
  Uint32 bg;
  int i, digit, length;

  b.x = 0;
  b.y = 0;
  b.w = SCREEN_W;
  b.h = SCREEN_CITY_AT;
  
  bg = SDL_MapRGB(screen->format, G_BLACK);
  SDL_FillRect(screen, &b, bg);
  SDL_UpdateRects(screen, 1, &b);

  SDL_BlitSurface( SPRITE[I_WAVE], NULL, screen, &IMAGE_LOCATION[I_WAVE] ); 
  SDL_UpdateRects(screen, 1, &IMAGE_LOCATION[I_WAVE]);

  length = (int)log10(number) + 1; /* number of digits in the wave number */
  length = MAX(length, 2); /* have a leading 0 for single digit numbers */

  r.x = IMAGE_LOCATION[I_WAVE].x + IMAGE_LOCATION[I_WAVE].w + (length * NUMBER_WAVE_W);
  r.y = IMAGE_LOCATION[I_WAVE].y;
  r.w = NUMBER_WAVE_W;
  r.h = NUMBER_WAVE_H;

  s.y = 0; 
  s.w = NUMBER_WAVE_W;
  s.h = NUMBER_WAVE_H;
    
  for ( i=length-1; i>=0; i-- )
  {
    digit = number % 10;
    number = number / 10;
    
    r.x -= NUMBER_WAVE_W;
    s.x = digit * NUMBER_WAVE_W;
  
    SDL_BlitSurface( SPRITE[I_WAVE_NUMBER], &s, screen, &r ); 
    SDL_UpdateRects(screen, 1, &r);
  }

  SDL_Delay(2000);
  SDL_FillRect( screen, &b, SDL_MapRGB(screen->format, G_BLACK) ); 
  SDL_UpdateRects(screen, 1, &b);
}

///////////////////////////////////////////////////////////////////////////

void show_end_of_wave_splash_screen( SDL_Surface *screen, Game *g )
{
  SDL_Rect r, s;
  int i, number, length, digit, num;

  
  number = g->wave_number + 1;
  length = (int)log10(number) + 1; /* number of digits in the wave number */
  length = MAX(length, 2); /* have a leading 0 for single digit numbers */

  r.x = (SCREEN_W - (SPRITE[I_EOW_WAVE]->w + SPRITE[I_EOW_COMPLETE]->w +
                     length * NUMBER_EOW_WAVE_W)) / 2;
  r.y = SCREEN_H / 2 - SPRITE[I_EOW_WAVE]->h * 2;
  r.w = SPRITE[I_EOW_WAVE]->w;
  r.h = SPRITE[I_EOW_WAVE]->h;

  SDL_BlitSurface( SPRITE[I_EOW_WAVE], NULL, screen, &r ); 
  SDL_UpdateRects(screen, 1, &r);

  r.x += SPRITE[I_EOW_WAVE]->w + length * NUMBER_EOW_WAVE_W;
  r.w = NUMBER_EOW_WAVE_W;
  r.h = NUMBER_EOW_WAVE_H;

  s.y = 0; 
  s.w = NUMBER_EOW_WAVE_W;
  s.h = NUMBER_EOW_WAVE_H;
    
  for ( i=length-1; i>=0; i-- )
  {
    digit = number % 10;
    number = number / 10;

    r.x -= NUMBER_EOW_WAVE_W;
    s.x = digit * NUMBER_EOW_WAVE_W;
  
    SDL_BlitSurface( SPRITE[I_EOW_WAVE_NUMBER], &s, screen, &r ); 
    SDL_UpdateRects(screen, 1, &r);
  }

  r.x += length * NUMBER_EOW_WAVE_W;
  r.w = SPRITE[I_EOW_COMPLETE]->w;
  r.h = SPRITE[I_EOW_COMPLETE]->h;
  SDL_BlitSurface( SPRITE[I_EOW_COMPLETE], NULL, screen, &r ); 
  SDL_UpdateRects(screen, 1, &r);

  SDL_Delay(500);

  /* draw cities left */
  r.x = (SCREEN_W * 2) / 3;
  r.y = SCREEN_H / 2;
  r.w = SCREEN_CITY_WIDTH;
  r.h = SCREEN_CITY_HEIGHT;

  num = 0;
  for ( i=0; i<6; i++ )
  {
    if ( g->city[i].alive )
    {
      r.x = r.x - SCREEN_CITY_WIDTH - 5;
      SDL_BlitSurface( SPRITE[I_CITY] , NULL, screen, &r );
      SDL_UpdateRects(screen, 1, &r);
      add_to_score( g, POINTS_BONUS_BASES_LEFT );
      update_scoreboard( screen, g, 1 );
      num++;
      SDL_Delay(250);
   }
  }
  
  /* calculate bonus points from cities */
  r.x = (SCREEN_W * 2) / 3;
  r.y +=  SCREEN_CITY_HEIGHT - SPRITE[I_EOW_x100]->h;
  r.w = SPRITE[I_EOW_x100]->w;
  r.h = SPRITE[I_EOW_x100]->h;

  SDL_BlitSurface( SPRITE[I_EOW_x100], NULL, screen, &r ); 
  SDL_UpdateRects(screen, 1, &r);

  number = num * POINTS_BONUS_BASES_LEFT;
  if ( number <= 0 )
    length = 1;
  else
    length = (int)log10(number) + 1; /* number of digits in the wave number */
  
  r.x += SPRITE[I_EOW_x100]->w + length * NUMBER_COLOURED_W;
  // r.y +=  SCREEN_CITY_HEIGHT - SPRITE[I_EOW_x100]->h;
  r.w = NUMBER_COLOURED_W;
  r.h = NUMBER_COLOURED_H;

  s.y = WHITE * NUMBER_COLOURED_H; 
  s.w = NUMBER_COLOURED_W;
  s.h = NUMBER_COLOURED_H;
 
  for ( i=length-1; i>=0; i-- )
  {
    digit = number % 10;
    number = number / 10;

    r.x -= NUMBER_COLOURED_W;
    s.x = digit * NUMBER_COLOURED_W;
  
    SDL_BlitSurface( SPRITE[I_COLOUR_NUMBER], &s, screen, &r ); 
    SDL_UpdateRects(screen, 1, &r);
  }
  SDL_Delay(500);

  /* draw missiles remaining */
  r.x = (SCREEN_W * 2) / 3 - 5;
  r.y = SCREEN_H / 2 + SCREEN_CITY_HEIGHT + 5;
  r.w = SPRITE[I_MISSILE]->w;
  r.h = SPRITE[I_MISSILE]->h;
  
  num = 0;
  for ( i=0; i<3; i++ )
  {
    while ( g->base[i].num_sams > 0)
    {
      r.x = r.x - SPRITE[I_MISSILE]->w;
      if ( num < 42 )
      {
        SDL_BlitSurface( SPRITE[I_MISSILE] , NULL, screen, &r );
        SDL_UpdateRects(screen, 1, &r);
      }
      add_to_score( g, POINTS_BONUS_SAMS_LEFT );
      g->base[i].num_sams--;
      draw_bases( screen, g, 1 );
      update_scoreboard( screen, g, 1 );
      num++;
      SDL_Delay(50);
    }
  }

  /* bonus points for missiles remaining */
  r.x = (SCREEN_W * 2) / 3;
  r.y += SPRITE[I_MISSILE]->h - SPRITE[I_EOW_x10]->h;
  r.w = SPRITE[I_EOW_x10]->w;
  r.h = SPRITE[I_EOW_x10]->h;

  SDL_BlitSurface( SPRITE[I_EOW_x10], NULL, screen, &r ); 
  SDL_UpdateRects(screen, 1, &r);

  number = num * POINTS_BONUS_SAMS_LEFT;
  if ( number <= 0 )
    length = 1;
  else
    length = (int)log10(number) + 1; /* number of digits in the wave number */
  
  r.x += SPRITE[I_EOW_x10]->w + length * NUMBER_COLOURED_W;
  // r.y +=  SCREEN_CITY_HEIGHT - SPRITE[I_EOW_x100]->h;
  r.w = NUMBER_COLOURED_W;
  r.h = NUMBER_COLOURED_H;

  s.y = WHITE * NUMBER_COLOURED_H; 
  s.w = NUMBER_COLOURED_W;
  s.h = NUMBER_COLOURED_H;
 
  for ( i=length-1; i>=0; i-- )
  {
    digit = number % 10;
    number = number / 10;

    r.x -= NUMBER_COLOURED_W;
    s.x = digit * NUMBER_COLOURED_W;
  
    SDL_BlitSurface( SPRITE[I_COLOUR_NUMBER], &s, screen, &r ); 
    SDL_UpdateRects(screen, 1, &r);
  }

 
  SDL_Delay(2000);

  SDL_FillRect( screen, &r, SDL_MapRGB(screen->format, G_BLACK) ); 
  SDL_UpdateRects(screen, 1, &r);
}

///////////////////////////////////////////////////////////////////////////

void add_to_score( Game *g, int points )
{
  int check_new_city, check_new_base;

  check_new_city = g->score / POINTS_NEW_CITY_AT;
  check_new_base = g->score / POINTS_NEW_BASE_AT;
  
  g->score += points;

  if (check_new_city < g->score / POINTS_NEW_CITY_AT) g->reserve_cities++;
  if (check_new_base < g->score / POINTS_NEW_BASE_AT) g->reserve_bases++;
  if (g->score > g->highscore)
  {
    g->highscore = g->score;
    g->scorecolor = GREEN;
  }
}

///////////////////////////////////////////////////////////////////////////

void read_rc_file( Game *g )
{
  char rcfilename[RCFILE_FILENAME_LENGTH];
  char line[RCFILE_MAX_LINE_SIZE];
  char *home;
  FILE *rcfile;
  
  home = getenv("HOME");

  if ( home == NULL )
  {
    home = (char *)calloc( sizeof(char), 5 );
    strcpy( home, "/tmp" );
  }

  sprintf(rcfilename, "%s/.missilecommandrc", home ) ;
  
  g->highscore = 0;
  g->data_dir  = NULL;

  rcfile = fopen( rcfilename, "r" ); 
  if (rcfile != NULL)
  {
    while ( fgets(line, RCFILE_MAX_LINE_SIZE, rcfile) != NULL )
    {
      if ( strncmp(line, RCFILE_HIGHSCORE, strlen(RCFILE_HIGHSCORE)) == 0 )
      {
        g->highscore = atoi( strchr(line,'=') + 1 );
      }
      else if ( strncmp(line, RCFILE_DATADIR, strlen(RCFILE_DATADIR)) == 0 )
      {
        g->data_dir = (char *)calloc( sizeof(char), RCFILE_MAX_LINE_SIZE );
        strcpy( g->data_dir, strchr(line,'=') + 1 );
        if ( g->data_dir[strlen(g->data_dir)-1] == '\n' )
          g->data_dir[strlen(g->data_dir)-1] = 0x00; /* strip newline */
      }
    }
    fclose(rcfile);
  }

  if ( g->highscore == 0 ) g->highscore = POINTS_DEFAULT_HIGH_SCORE;
  
  if ( strcmp(getenv("HOME"), home) != 0 )
    free(home);
}

///////////////////////////////////////////////////////////////////////////

void write_rc_file( Game *g )
{
  char rcfilename[RCFILE_FILENAME_LENGTH];
  char *home;
  FILE *rcfile;
  
  home = getenv("HOME");

  if ( home == NULL )
  {
    home = (char *)calloc( sizeof(char), 5 );
    strcpy( home, "/tmp" );
  }

  sprintf(rcfilename, "%s/.missilecommandrc", home ) ;
  
  rcfile = fopen( rcfilename, "w" ); 
  if (rcfile != NULL)
  {
    fprintf( rcfile, "%s=%s\n",  RCFILE_DATADIR,   g->data_dir  );
    fprintf( rcfile, "%s=%li\n", RCFILE_HIGHSCORE, g->highscore );
    fclose(rcfile);
  }
  
  if ( strcmp(getenv("HOME"), home) != 0 )
    free(home);
}

///////////////////////////////////////////////////////////////////////////

/* Try and find the data dir.  Look in:
 *  A dir given on the command line.
 *  A dir set in the users ~/.missilecommandrc settings file.
 *  The dir set at compile time (in the macro DATA).
 *  A dir specified in /etc/missilecommand.conf
 *  In a dir called "data" relative to where the program is executed from.
 *  In a dir called "data" in the current dir.
 *  Give up and use the current dir.
 */
void set_data_dir( Game *g, char *hint1, char* hint2 )
{
  DIR  *d;
  char *s, *p;  /* meaningful variable names */
  char *path_ends;
  
  //printf("looking for data dir...\n");

  /* given to us on the commandline? */
  if ( hint1 != NULL )
  {
    if ( (d = opendir(hint1)) != NULL )
    {
      closedir(d);
      g->data_dir = (char *)malloc( sizeof(char) * strlen(hint1) + 1 );
      strcpy(g->data_dir, hint1);
      //printf("...found in dir given on command line\n");
      return; 
    }
    printf("ERROR: reading data directory\n");
    perror(hint1);
  }
  
  /* try the dir in the users rc file. */
  if ( g->data_dir != NULL )
  {
    if ( (d = opendir(g->data_dir)) != NULL )
    {
      closedir(d);
      //printf("...found in dir given in rc file\n");
      return; 
    }
    /* give error msg if other than "not found" */
    if ( errno != ENOENT )
    {
      printf("ERROR: reading data directory\n");
      perror(g->data_dir);
    }
    //printf("   data dir '%s' given in rc file is incorrect\n", g->data_dir);
    free(g->data_dir); /* an incorrect data_dir line exists in the rc file */
    g->data_dir = NULL;
  }
  
  /* try the #defined data dir */
  if ( (d = opendir(DATA)) != NULL )
  {
    closedir(d);
    g->data_dir = (char *)malloc( sizeof(char) * (strlen(DATA) + 1) );
    strcpy( g->data_dir, DATA );
    //printf("...found from the DATA macro\n");
    return;
  }
  /* give error msg if other than "not found" */
  if ( errno != ENOENT )
  {
    printf("ERROR: reading data directory\n");
    perror(DATA); 
  }

  /* see if its given in /etc/missilecommand.conf */
  if ( (s = get_data_from_etc()) != NULL )
  {
    g->data_dir = s;
    return;
  }
  
  /* try the dir the game is executed from */
  path_ends = strrchr(hint2, '/');
  if ( path_ends != NULL )
  {
    s = (char *)calloc( sizeof(char), strlen(hint2) + 1 );
    p = (char *)calloc( sizeof(char), strlen(hint2) + 7 );
    strncpy( s, hint2, strlen(hint2) - strlen(path_ends) );
    sprintf( p, "%s/data", s);
    free(s);
    if ( (d = opendir(p)) != NULL )
    {
      closedir(d);
      g->data_dir = p;
      //printf("...found in game executed from dir\n");
      return;
    }
    /* give error msg if other than "not found" */
    if ( errno != ENOENT )
    {
      printf("ERROR: reading data directory\n");
      perror(p);
    }
    free(p); 
  }

  /* try the current dir */
  if ( (d = opendir("data")) != NULL )
  {
    closedir(d);
    g->data_dir = (char *)malloc( sizeof(char) * 5 ); /* "data" plus null */
    strcpy( g->data_dir, "data" );
    // printf("...found in pwd\n");
    return;
  }
  /* give error msg if other than "not found" */
  if ( errno != ENOENT )
  {
    printf("ERROR: reading data directory\n");
    perror("data"); 
  }
  
  /* all reasonable effort expended */
  g->data_dir = (char *)malloc( sizeof(char) * 2 );
  g->data_dir[0] = '.';
  g->data_dir[1] = 0x00;

  printf("\nData directory not found in any of the expected places, "
         "using \".\"\n\n");
  printf("Run Missile Command with "
         "\"missile --set-datadir=location_of_data\" "
         "to set a data directory\n\n");
}

///////////////////////////////////////////////////////////////////////////

/* We need some way of specifying the data directory if its not the default as
 * compiled, but is variable if the program is distributed as binary.  Check
 * for /etc/missilecommand.conf if we haven't found a valid data dir yet
 */
char *get_data_from_etc()
{
  FILE *etcconffile;
  char *datadir;
  char line[RCFILE_MAX_LINE_SIZE];

  datadir = NULL;
  etcconffile = fopen( "/etc/missilecommand.conf", "r" ); 
  if (etcconffile != NULL)
  {
    while ( fgets(line, RCFILE_MAX_LINE_SIZE, etcconffile) != NULL )
    {
      if ( strncmp(line, RCFILE_DATADIR, strlen(RCFILE_DATADIR)) == 0 )
      {
        datadir = (char *)calloc( sizeof(char), RCFILE_MAX_LINE_SIZE );
        strcpy( datadir, strchr(line,'=') + 1 );
        if ( datadir[strlen(datadir)-1] == '\n' )
          datadir[strlen(datadir)-1] = 0x00; /* strip newline */
      }
    }
    fclose(etcconffile);
  }

  return datadir;
}

///////////////////////////////////////////////////////////////////////////

void screen_shot( SDL_Surface *screen ) /* cause I can =) */
{
  DIR *d;
  struct dirent *entry;
  char *home;
  int next_number, check;
  char *filename, *fullname;
  
  /* save screenshots in home dir or default to /tmp */
  home = getenv("HOME");
  if ( home == NULL )
  {
    home = (char *)calloc( sizeof(char), 5 );
    strcpy( home, "/tmp" );
  }
  d = opendir( home ) ;

  if ( d == NULL )
  {
    printf("can't read from %s\n", home);
    return;
  }
  
  next_number = 0;
  check = -1;
  /* give the screen shot a number one higher than the biggest we have */
  while ( (entry = readdir(d)) != NULL )
  {
    sscanf( entry->d_name, SCREENSHOT_FORMAT, &check );
    if ( check >= next_number )
      next_number = check + 1;
  }
  
  closedir(d);

  filename = (char *)malloc( sizeof(char) * 256);
  fullname = (char *)malloc( sizeof(char) * 256);
  sprintf( filename, SCREENSHOT_FORMAT, next_number );
  sprintf( fullname, "%s/%s", home, filename );  
  printf("wrote %s\n", fullname);
  SDL_SaveBMP( screen, fullname );

  free(filename);
  free(fullname);
  
  if ( strcmp(getenv("HOME"), home) != 0 )
    free(home);
}

///////////////////////////////////////////////////////////////////////////

void toggle_pause( SDL_Surface *screen, Game *g )
{
  static SDL_Surface *save_under = NULL;

  if ( ! save_under )
    save_under = SDL_CreateRGBSurface( SDL_HWSURFACE | SDL_SRCCOLORKEY,
                                       IMAGE_LOCATION[I_PAUSED].w,
                                       IMAGE_LOCATION[I_PAUSED].h,
                                       screen->format->BitsPerPixel,
                                       screen->format->Rmask,
                                       screen->format->Gmask,
                                       screen->format->Bmask,
                                       screen->format->Amask );
  
  if ( g->is_paused )
  {
    g->diff_time = SDL_GetTicks() - g->pause_time;
    SDL_BlitSurface(save_under, NULL, screen, &IMAGE_LOCATION[I_PAUSED]);
    SDL_UpdateRects(screen, 1, &IMAGE_LOCATION[I_PAUSED]);
  }
  else
  {
    g->pause_time = SDL_GetTicks() - g->diff_time;
    SDL_BlitSurface(screen, &IMAGE_LOCATION[I_PAUSED], save_under, NULL); 
    SDL_BlitSurface(SPRITE[I_PAUSED], NULL, screen, &IMAGE_LOCATION[I_PAUSED]);
    SDL_UpdateRects(screen, 1, &IMAGE_LOCATION[I_PAUSED]);
  }
  
  g->is_paused = ! g->is_paused;
}

///////////////////////////////////////////////////////////////////////////

void on_signal_quit(int s)
{
  RUNNING = 0;
  exit(0);
}

///////////////////////////////////////////////////////////////////////////

void set_missile_icon( Game *g )
{
  SDL_Surface *icon;
  char filename[RCFILE_MAX_LINE_SIZE + 30];

  sprintf(filename, "%s/%s", g->data_dir, ICON_FILENAME );
 
  icon = IMG_Load( filename );
  if ( icon != NULL )
    SDL_WM_SetIcon( icon, NULL );
  else
    fprintf(stderr, "Couldn't set icon\n");
}

///////////////////////////////////////////////////////////////////////////

void clean_up()
{
  //  printf("Exiting...\n");
}

///////////////////////////////////////////////////////////////////////////

void run_a_wave( SDL_Surface *screen, Game *g )
{
  SDL_Event event;
  int earlier, delay;
  //  SDL_Rect c;
  SDLKey key;
  Missile *m;
  Blast *b;
  int mouse_x, mouse_y, base;
  //int somethings_alive;
  int game_on = 1;
  //int i;
 
  do 
  {
    earlier = get_game_time(g);

    while (SDL_PollEvent(&event))
      switch (event.type)
      {
        case SDL_KEYDOWN:
          key = event.key.keysym.sym;
          
          if (key == QUIT)
          {
            g->currentwave.endtime = 0;
            game_on = 0;
            g->gameover = 1;
          }
          else if (key == SKIPWAVE)
            g->wave_number++;
          else if (key == FORCENEXT)
          {
            game_on = 0;
            g->currentwave.endtime = 0;
            g->wave_number--;
          }
          else if ( (key == SCREENSHOT) || (key == SDLK_z) )
            screen_shot( screen );
          else if (key == PAUSE)
            toggle_pause(screen, g);

          /* disallow any other events while paused
           * (although it is nice to pause and pick your targets =) */
          if ( g->is_paused ) break;
          
          SDL_GetMouseState( &mouse_x, &mouse_y);
          base = -1;
          if ((key == SDLK_LCTRL) || (key == SDLK_RCTRL) ||
              (key == SDLK_LSHIFT) || (key == SDLK_RSHIFT) ||
              (key == SDLK_SPACE)) /* lotsa choice */
            base = get_closest_sam( g, mouse_x );
          else if (key == SDLK_1) base = 0;
          else if (key == SDLK_2) base = 1;
          else if (key == SDLK_3) base = 2;
          if (base != -1)
            fire_sam( &(g->currentwave.sam), &(g->base[base]),
                      get_game_time(g),
                      mouse_x, MIN(SCREEN_CITY_AT, mouse_y) );
          break;            
        case SDL_MOUSEMOTION: /* get your rat outta my city */
          if ( event.motion.y > SCREEN_CITY_AT )
            SDL_WarpMouse( event.motion.x, SCREEN_CITY_AT );
          break;
        case SDL_MOUSEBUTTONDOWN:
          if ( ! g->is_paused )
            fire_sam( &(g->currentwave.sam),
                      &(g->base[(event.button.button-1)%3]), get_game_time(g),
                      event.button.x, MIN(SCREEN_CITY_AT, event.button.y) );
          break;
        case SDL_QUIT: 
          exit(0);
        default:
          break;
      }

    if ( ! g->is_paused )
    {
      /* incoming */
      for (m = g->currentwave.missile; m != NULL; m = m->next)
      {
        collision_detection( m, g );

        if ( m->type == SMART )
          draw_smartbomb(screen, g, m);
        else
          draw_line( screen, g, m );

        /* handle expiration */
        if ( m->detonated )
        {
          if ( (m->type == MIRV) && (!m->has_split) )
          {
            m->detonated = 0; /* give it a frames more life */
            trigger_mirv( &(g->currentwave.missile), g->wave_number,
                          get_game_time(g), m );
          }
          else if ( m->type == SMART )
            zap_smartbomb( screen, m);
          else
            zap_line( screen, m );
        }
      }

      /* outgoing */
      for (m = g->currentwave.sam; m != NULL; m = m->next)
      {
        draw_line( screen, g, m );
        if ( m->detonated )
        {
          zap_line( screen, m );
          create_blast_from_sam(&(g->currentwave.blast), m, get_game_time(g));
        }
      }

      /* boom */
      for (b = g->currentwave.blast; b != NULL; b = b->next)
      {
        draw_detonation( screen, g, b );
      }
    
      draw_cities( screen, g, 0 );
      draw_bases( screen, g, 0 );
      update_scoreboard( screen, g, 0 );

      /* cleanup */
      delete_used_missiles( &(g->currentwave.missile) );
      delete_used_missiles( &(g->currentwave.sam) );
      delete_finished_explosions( &(g->currentwave.blast), get_game_time(g) );

      /* do we die once everythings gone? or wait till the end of the wave
      somethings_alive = 0;
      for ( i=0; i<3; i++ ) somethings_alive |= g->base[i].alive;
      if ( !somethings_alive ) // only keep checking if all bases are dead
      {
        for ( i=0; i<6; i++ ) somethings_alive |= g->city[i].alive;
        if ( !somethings_alive ) g->currentwave.endtime = 0;
      }
      */
    }
    
    //printf("%i rectangles to redraw\r", LAST_RECT );
    if ( LAST_RECT == -1 ) LAST_RECT = 1; 
    SDL_UpdateRects(screen, LAST_RECT, RECTS_TO_UPDATE);
    LAST_RECT = 0;
    //SDL_UpdateRect(screen, 0, 0, 0, 0);
    
    delay = 1000/FPS - (get_game_time(g) - earlier);
    //printf("% 3i fps  \r", 1000/(1000/FPS - delay) );
    //fflush(stdout);
    SDL_Delay( MAX(0,delay) );
  }
  while ( game_on && ((g->currentwave.missile != NULL) ||
                      (g->currentwave.blast   != NULL)) );
  //   while ( (g->currentwave.endtime >= earlier) ||
  //           (g->currentwave.blast   != NULL) );

}

///////////////////////////////////////////////////////////////////////////

int main(int argc, char *argv[])
{
  SDL_Surface *screen;
  int flags;
  int i;
  SDL_Cursor *cursor;
  SDL_Event event;
  SDLKey key;
  static Game thegame;
  int allcitiesdead;
  char *new_datadir;
  int use_big_cursor = 0;
  
  if ( SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_NOPARACHUTE) < 0 )
  {
    fprintf(stderr, "Unable to init SDL: %s\n", SDL_GetError());
    exit(1);
  }
  atexit(SDL_Quit);
  atexit(clean_up);
  signal(SIGINT, on_signal_quit);

  srandom( time(NULL) ); /* initialise random number generator */

  flags = SDL_HWSURFACE | SDL_ANYFORMAT;
  new_datadir = NULL;
  
  /* Command line crappage */
  for ( i=1; i<argc; i++ )
  {
    if ((strcmp(argv[i], ARGV_FULLSCREEN) == 0) ||
        (strcmp(argv[i], ARGV_FULLSCREEN_SHORT) == 0))
      flags |= SDL_FULLSCREEN;
    else if ((strncmp(argv[i], ARGV_SET_DATADIR,strlen(ARGV_SET_DATADIR)) == 0)
             || (strncmp(argv[i], ARGV_SET_DATADIR_SHORT, strlen(ARGV_SET_DATADIR_SHORT)) == 0))
      new_datadir = strchr(argv[i], '=') + 1;
    else if ((strcmp(argv[i], ARGV_BIG_CURSOR) == 0) ||
             (strcmp(argv[i], ARGV_BIG_CURSOR_SHORT) == 0))
      use_big_cursor = 1;
    else
    {
      printf("Missile Command v%s by Julian Peterson\n\n", VERSION);
      printf("Usage: missile [options]\n"
             "  -f, --fullscreen            : play game using the full screen.\n"
             "  -d, --set-datadir=<datadir> : look for game data in dir <datadir>.\n"
             "  -b, --bigcursor             : use a larger crosshair.\n"
             "  -h, --help                  : this message\n"
             "\n"
             "Controls:\n"
             "  Use the mouse to aim.\n"
             "  1, left mouse button        : fire from the left SAM site.\n"
             "  2, middle mouse button      : fire from the centre SAM site.\n"
             "  3, right mouse button       : fire from the right SAM site.\n"
             "  ctrl, shift, space          : fire from the SAM site closest to the target\n"
             "  ESC                         : quit\n"
             "  Print Screen                : take a screenshot\n"
             "  Pause                       : pause the game\n"
             "\n"
             "Bug reports to weaver@earthcorp.com\n");
      exit(0);
    }
  }

  /* Window and icon titles */
  SDL_WM_SetCaption("Missile Command", "Missile Command");
 
  /* Get my funky crosshairs */
  if ( use_big_cursor )
    cursor = SDL_CreateCursor( BIG_CURSOR, BIG_MASK,
                               BIG_CROSSHAIR_W, BIG_CROSSHAIR_H,
                               BIG_CROSSHAIR_HOT_X, BIG_CROSSHAIR_HOT_Y );
  else
    cursor = SDL_CreateCursor( CURSOR, MASK, CROSSHAIR_W, CROSSHAIR_H,
                               CROSSHAIR_HOT_X, CROSSHAIR_HOT_Y );
  SDL_SetCursor(cursor); 

  
  /* data initialisation */
  read_rc_file( &thegame );
  set_data_dir( &thegame, new_datadir, argv[0] );
  //printf("data is in %s\n", thegame.data_dir);
  thegame.gameover = 1;
  
  /* set the runtime icon used by some window managers */
  set_missile_icon( &thegame );  

  /*** attention ***/
  /* not sure which is better... test this:   */
  //  screen = SDL_SetVideoMode(SCREEN_W, SCREEN_H, 16, flags);
  /* or */
  screen = SDL_SetVideoMode(SCREEN_W, SCREEN_H, 0, flags);
  if ( screen == NULL )
  {
    fprintf(stderr, "Unable to set %ix%i video mode: %s\n",
            SCREEN_W, SCREEN_H, SDL_GetError());
    exit(1);
  }
  SDL_SetColors( screen, G_COLOUR, 0, G_NUMBER_COLOURS );

  /*
    if ( SDL_MUSTLOCK(screen) )
    printf("screen must be locked when writing to video memory\n");
    printf("screen is %ix%i with %ibbp\n",
    screen->w, screen->h, screen->format->BitsPerPixel);
  */

  load_images( &thegame );

  /* paint the screen black */  
  SDL_FillRect( screen, NULL, SDL_MapRGB(screen->format, G_BLACK) ); 
  SDL_UpdateRect(screen, 0, 0, 0, 0);
 
  while (RUNNING)
  {
    show_game_splash_screen( screen );
    
    while (SDL_PollEvent(&event))
    {
      switch (event.type)
      {
        case SDL_KEYDOWN:
          key = event.key.keysym.sym;
          if (key == QUIT)
            RUNNING = 0;
          else if (key == STARTGAME)
          {
            initgame_data( &thegame );
            SDL_FillRect(screen, NULL, 0); 
            SDL_UpdateRect(screen, 0, 0, 0, 0);           
          }
          else if ( key == SCREENSHOT )
            screen_shot( screen );
          break;
        case SDL_MOUSEMOTION: 
          break;
        case SDL_MOUSEBUTTONDOWN:
          break;
        case SDL_QUIT: 
          exit(0);
        default:
          break;
      }
    }

    while ( !thegame.gameover )
    {
      show_wave_splash_screen( screen, thegame.wave_number + 1 );
      initwave_data( &thegame, thegame.wave_number );
      draw_cities( screen, &thegame, 0 );
      draw_bases( screen, &thegame, 0 );
  
      /* flush queued events (except quit) */
      while (SDL_PollEvent(&event))
        if (event.type == SDL_QUIT)
          exit(0);
          
      run_a_wave( screen, &thegame);

      if ( thegame.currentwave.endtime )
        show_end_of_wave_splash_screen( screen, &thegame );
      
      freewave_data( &thegame );
      thegame.wave_number++;
      
      allcitiesdead = 1;
      for ( i=0; i<6; i++ )
        allcitiesdead &= !thegame.city[i].alive;

      if (allcitiesdead)
        thegame.gameover = 1;

      if (thegame.gameover)
      {
        show_game_over_screen( screen );
        write_rc_file( &thegame );
      }
    }
    
    if ( LAST_RECT == -1 ) LAST_RECT = 1; 
    SDL_UpdateRects(screen, LAST_RECT, RECTS_TO_UPDATE);
    LAST_RECT = 0;
    SDL_Delay( 1000/FPS );
  }

  SDL_FreeCursor(cursor); 

  return 0;
}

///////////////////////////////////////////////////////////////////////////
