// 
// missile.h -- 
// Author          : Julian Peterson <weaver@earthcorp.com>
// Created On      : Tue Feb 22 23:36:26 2000
// Last Modified By: Julian Peterson <weaver@earthcorp.com>
// Last Modified On: Fri Mar 10 19:34:10 2000
// Update Count    : 240
// Status          : Yeah baby yeah!
// $Id: missile.h,v 0.93 2000/03/11 00:34:25 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
//
 
#ifndef _julian_missile_h_
#define _julian_missile_h_

/*
 *  Dependencies on other include files:
 */
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>
#include <signal.h>
#include <string.h>
//#include <sys/types.h>
#include <dirent.h>
#include <SDL/SDL.h>
//#include <SDL/SDL_thread.h>
#include <SDL/SDL_image.h>
#include <SDL/SDL_ttf.h>



#ifdef __cplusplus
extern "C" {
#endif


#ifndef DATA
#define DATA "/usr/local/share/missile"
#endif

/*
 *  Constants and macros:
 */

#define MAX(A,B) ((A)>(B)?(A):(B))
#define MIN(A,B) ((A)<(B)?(A):(B))
  
#define ABS(A) ((A)>0?(A):-(A))
#define SQR(A) ((A)*(A))          // just don't SQR(i++);
#define RND(A) ((int)((A) + 0.5)) // only good for +ve numbers

#define REALLY_SMALL 0.0000001
#define NEAR_ZERO(A) ((-REALLY_SMALL < (A)) && ((A) < REALLY_SMALL))



#define FPS 30       /* maximum fps to render */

#define SCREEN_W 640
#define SCREEN_H 480


  
// from the SDL video examples =)
#define DRAWPIXEL(screen,x,y,colour)                                   {  \
    Uint8 bpp = screen->format->BytesPerPixel;                            \
    Uint8 *data = ((Uint8 *)screen->pixels) +                             \
                  (y) * screen->pitch + (x) * bpp;                        \
     switch(bpp)                                                          \
    {                                                                     \
      case 1:                                                             \
        *((Uint8 *)(data)) = (Uint8)colour;                               \
        break;                                                            \
      case 2:                                                             \
        *((Uint16 *)(data)) = (Uint16)colour;                             \
        break;                                                            \
      case 3:                                                             \
      {                                                                   \
        Uint8 r, g, b;                                                    \
        r = (colour>>screen->format->Rshift)&0xFF;                        \
        g = (colour>>screen->format->Gshift)&0xFF;                        \
        b = (colour>>screen->format->Bshift)&0xFF;                        \
        *((data)+screen->format->Rshift/8) = r;                           \
        *((data)+screen->format->Gshift/8) = g;                           \
        *((data)+screen->format->Bshift/8) = b;                           \
      }                                                                   \
      break;                                                              \
      case 4:                                                             \
        *((Uint32 *)(data)) = (Uint32)colour;                             \
        break;                                                            \
    }                                                                     \
 }


#define INVERT_SURFACE(surface)                                        {  \
    int i;                                                                \
    int length = (surface->h -1) * surface->pitch +                       \
                 (surface->w -1) * surface->format->BytesPerPixel;        \
    if ( !(SDL_MUSTLOCK(surface) && (SDL_LockSurface(surface) < 0)) )     \
    {                                                                     \
      for (i=0; i<length; i++)                                            \
        *(((Uint8 *)surface->pixels) + i) ^= 0xFF;                        \
      if ( SDL_MUSTLOCK(surface) )  SDL_UnlockSurface(surface);           \
      SDL_SetColorKey( surface, SDL_SRCCOLORKEY,                          \
                       SDL_MapRGB(surface->format, 0xFF, 0xFF, 0xFF));    \
    }                                                                     \
  }


  
//
// SAMs move at SAM_VELOCITY.
// When detonate, the blast radius increases at a rate
//   of SAM_EXPLOSION_VELOCITY.
// When the blast radius reaches SAM_EXPLOSION_RADIUS, the blast starts
//   shrinking back to zero.
#define SAM_VELOCITY                500.0 /* pixels/second */
#define SAM_EXPLOSION_VELOCITY       35.0 /* pixels/second */
#define SAM_EXPLOSION_RADIUS         40   /* pixels */

#define SAMSITE_BASE_NUM_SAMS        15
#define SAMSITE_FONT_HEIGHT          10
#define SAMSITE_SHOOT_FROM           (SCREEN_CITY_BOTTOM_AT - 20)
#define SAMSITE_ZONE_1               (SCREEN_W * 1.0/3.0)
#define SAMSITE_ZONE_2               (SCREEN_W * 0.50)
#define SAMSITE_ZONE_3               (SCREEN_W * 2.0/3.0)
    
#define SCREEN_CITY_BOTTOM_AT        (STATUS_AT_Y - 4)
#define SCREEN_CITY_HEIGHT           40
#define SCREEN_CITY_WIDTH            60
#define SCREEN_CITY_AT             (SCREEN_CITY_BOTTOM_AT - SCREEN_CITY_HEIGHT)
#define SCREEN_CITY_HOTSPOT_AT     (SCREEN_CITY_BOTTOM_AT - 5)


#define MISSILE_SMART_SPEED_MULT      1.5
#define MISSILE_BASE_VELOCITY(T) (40.0*((T==SMART)?MISSILE_SMART_SPEED_MULT:1))
#define MISSILE_VELOCITY(T,L)    (MISSILE_BASE_VELOCITY(T) + ((L) * 2))
#define MISSILE_EXPLOSION_VELOCITY   35.0 /* pixels/second */
#define MISSILE_EXPLOSION_RADIUS     30   /* pixels */
#define MISSILE_CHANCE_MIRV(L,P)    MIN( ((L)/2) * (P + 1), 10 )       
#define MISSILE_MIN_MIRVS             2
#define MISSILE_NUMBER_MIRVS(L)     (random() % MIN((L)/5 + 1, 10) \
                                     + MISSILE_MIN_MIRVS)
#define MISSILE_MIRV_MIN_HEIGHT     (SCREEN_CITY_BOTTOM_AT - 100)     
  //#define MISSILE_CHANCE_SMART(L,P)  MIN( ((L)/3) * (P + 1), 8 )
#define MISSILE_CHANCE_SMART(L,P)   MIN( ((L)/3) * (P + 1), 10 )
#define MISSILE_SMART_R_INFLUENCE   100 /* blasts < dist effect direction */
#define MISSILE_SMART_DRIFT         10 /* percentage of velocity */

#define MISSILE_SMART_MIN_VX       -0.6 /* range of values of speeds as      */
#define MISSILE_SMART_MAX_VX        0.6 /*  modifiable by proximity to       */
#define MISSILE_SMART_MIN_VY        0.3 /*  explosions as percentages of max */
#define MISSILE_SMART_MAX_VY        1.2 /*  velocity                         */

// calculations for forces acting on smart bombs  
#define MK_VARIANCE(min,max)       (((max) - (min)) / 2.0) 
#define MK_SHIFT(min,max)         ((((max) - (min)) / 2.0) + (min)) 
#define X_VARIANCE        MK_VARIANCE(MISSILE_SMART_MIN_VX,MISSILE_SMART_MAX_VX)
#define X_SHIFT           MK_SHIFT(MISSILE_SMART_MIN_VX,MISSILE_SMART_MAX_VX)
#define Y_VARIANCE        MK_VARIANCE(MISSILE_SMART_MIN_VY,MISSILE_SMART_MAX_VY)
#define Y_SHIFT           MK_SHIFT(MISSILE_SMART_MIN_VY,MISSILE_SMART_MAX_VY)
#define MISSILE_SMART_SHIFT_X(F) ((F) * X_VARIANCE + X_SHIFT)  
#define MISSILE_SMART_SHIFT_Y(F) ((F) * Y_VARIANCE + Y_SHIFT)
  

  
/* Magic */
#define PHASE_NUMBER_PHASES(L)     MIN( ((L) / 10) + 2, 4 )
#define PHASE_NUMBER_MISSILES(L)   (5 + ((L) / 2) + random() % 5)
#define PHASE_MISSILE_TIMING(L)   (2000 / (1 + (L)/5))
#define PHASE_MISSILE_MIN_GAP(L)  (PHASE_MISSILE_TIMING(L) / (1 + (L)/2))
#define PHASE_MISSILE_NEXT(L)     (PHASE_MISSILE_MIN_GAP(L)                   \
                                   + (random() % (1 + PHASE_MISSILE_TIMING(L) \
                                                - PHASE_MISSILE_MIN_GAP(L))))
/* delay between phases (ms) */
#define PHASE_INTERVAL(L)         (SCREEN_H/MISSILE_VELOCITY(REGULAR,L) * 500)
#define PHASE_WAIT_AT_END          100



  
#define MISC_EXPLOSION_VELOCITY      35.0 /* pixels/second */
#define MISC_EXPLOSION_RADIUS        20   /* pixels */


  

#define STATUS_SIZE                  19 
#define STATUS_AT_Y                  (SCREEN_H - STATUS_SIZE)
#define STATUS_LEVEL_AT_X            0
#define STATUS_SCORE_AT_X            (SCREEN_W / 3)
#define STATUS_HIGHSCORE_AT_X        ((3 * SCREEN_W) / 4)
#define STATUS_SPACE                 10
#define STATUS_FONT_HEIGHT           16
  

#define SPLASH_LARGE_FONT_HEIGHT     50
#define SPLASH_SMALL_FONT_HEIGHT     20


#define POINTS_REGULAR_MISSILE       10
#define POINTS_MIRV_MISSILE          20
#define POINTS_SMART_MISSILE         50
#define POINTS_BONUS_SAMS_LEFT       10
#define POINTS_BONUS_BASES_LEFT     100
#define POINTS_NEW_CITY_AT         5000

#define POINTS_DEFAULT_HIGH_SCORE  1000

  
#define RCFILE_FILENAME_LENGTH      256
#define RCFILE_MAX_LINE_SIZE        256
#define RCFILE_HIGHSCORE            "Highscore"
#define RCFILE_DATADIR              "Data"

  
#define ARGV_FULLSCREEN             "--fullscreen"
#define ARGV_SET_DATADIR            "--set-datadir"

  
#define SCREENSHOT_FORMAT           "missile%03d.bmp"
  



const SDLKey QUIT                 = SDLK_ESCAPE;
const SDLKey STARTGAME            = SDLK_RETURN;
const SDLKey SCREENSHOT           = SDLK_PRINT;

// cheats
const SDLKey SKIPLEVEL            = SDLK_i;
const SDLKey FORCENEXT            = SDLK_u;


// data for the crosshair cursor
Uint8 CURSOR[] = { 0, 0, 0,  0,  0, 0, 0, 0 };
Uint8 MASK[]   = { 8, 8, 8, 119, 8, 8, 8, 0 };


  
/*
 *  Type declarations:
 */

typedef enum { SAM = 0, REGULAR = 1, MIRV = 2, SMART = 3 } MissileFlavour;

  
typedef struct
{
  int x, y;
} Point;

typedef struct
{
  SDL_Rect r;
  SDL_Surface *img;
} Text;

typedef struct _Blast_Tag
{
  int    x, y;             // centre
  Uint32 created_at;       // time of creation
  Uint32 max_at;           // time at which radius == maxradius
  Uint32 end_at;
  double velocity;         // expansion velocity
  int    radius;           // current radius 
  int    maxradius; 
  struct _Blast_Tag *next; // blasts are in a linked list, this is the next one
} Blast;

typedef struct _Missile_Tag
{
  MissileFlavour type;
  Uint32 created_at;
  Uint32 end_at;
  Point *path;
  int    path_length;
  int    at;                   // current location (path index)
  int    velocity;
  double vx, vy;
  double x, y;                 // location for better than pixel resolution
  int    detonated;
  int    has_split;            // applies only to MIRVs
  struct _Missile_Tag *next;
} Missile;

typedef struct
{
  int alive;
  int location; // x position on screen
  int num_sams;
} SamSite;

typedef struct
{
  int alive;
  int location; // x position on screen
} City;

typedef struct
{
  long int  starttime;
  long int  endtime;
  int       num_phases;
  int      *num_missiles; // array size num_phases containing # missiles/phase
  int       totalmissiles;
  Missile  *missile;
  Missile  *sam;
  Blast    *blast;
} Level;

typedef struct
{
  long int    score;
  int         level_number;
  SamSite     base[3];
  City        city[6];
  int         reserve_cities;
  Level       currentlevel;
  int         gameover;
  long int    highscore;
  char       *data_dir;
  SDL_Color  *scorecolor;
} Game;



/*
 *  Function prototypes:
 */

Point* lineBresenham(int *size, int x0, int y0, int x1, int y1);

SDL_Surface *make_circle( SDL_Surface *screen, int radius, Uint8 r, Uint8 g, Uint8 b );
SDL_Surface *diff_blast( SDL_Surface *screen, int old_radius, int new_radius );


void draw_line(SDL_Surface *screen, Missile *m);
void zap_line(SDL_Surface *screen, Missile *m);
void draw_smartbomb(SDL_Surface *screen, Game *g, Missile *m);
void zap_smartbomb(SDL_Surface *screen, Missile *m);


void draw_cities( SDL_Surface *screen, Game *g, int force );
void draw_bases( SDL_Surface *screen, Game *g, int force );
int get_closest_sam( Game *g, int x );

void fire_sam(Missile **ml, SamSite *site, Uint16 at_x, Uint16 at_y);
void fire_missile_from_xy(Missile **ml, int level, Uint32 time,
                          MissileFlavour flavour, int x0, int y0);
#define fire_missile(M,L,T,F) fire_missile_from_xy(M,L,T,F,random()%SCREEN_W,0)
void trigger_mirv( Missile **ml, int level, Missile *m );


void collision_detection( Missile *m, Game *g );

  
void delete_used_missiles( Missile **missile_list );
void delete_finished_explosions( Blast **blast_list );


void create_blast_from_sam( Blast **blast_list, Missile *s );
void create_blast_from_missile( Blast **blast_list, Missile *m );
void create_blast_at( Blast **blast_list, int t, int x, int y );

void draw_detonation( SDL_Surface *screen, Blast *blast );


void add_to_score( Game *g, int points );
void update_scoreboard( SDL_Surface *screen, Game *g, int force );

void load_images( Game *g );
void load_fonts( Game *g );

void initgame_data( Game *g );

void show_game_splash_screen( SDL_Surface *screen );
void show_level_splash_screen( SDL_Surface *screen, int number );
void show_end_of_level_splash_screen( SDL_Surface *screen, Game *g );
void show_game_over_screen( SDL_Surface *screen );

void initlevel_data( Game *g, int levelnumber );
void freelevel_data( Game *g );

void run_a_level( SDL_Surface *screen, Game *g );

void read_rc_file( Game *g );
void write_rc_file( Game *g );

void set_data_dir( Game *g, char *hint1, char *hint2 );

void screen_shot( SDL_Surface *screen );

void UpdateRect(SDL_Rect *r);

void on_signal_quit(int s);
void clean_up();


#ifdef __cplusplus
};
#endif

#endif /* _julian_missile_h_ */
