/* 
 * 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: Tue Jun 27 21:29:46 2000
 * Update Count    : 315
 * Status          : Yeah baby yeah!
 * $Id: missile.h,v 0.98 2000/04/12 20:26:14 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 <errno.h>
#include <dirent.h>
#include <SDL/SDL.h>
#include <SDL/SDL_image.h>



#ifdef __cplusplus
extern "C" {
#endif


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

#ifndef VERSION
#define VERSION "X"
#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;                                                            \
    }                                                                     \
 }

  
/*
 * 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_SPEED_MULT(T)       (((T==SMART)?1.5:1) )
  //#define MISSILE_BASE_VELOCITY(T) (35.0*MISSILE_SPEED_MULT(T))
  //#define MISSILE_VELOCITY(T,L)    (MISSILE_BASE_VELOCITY(T) + ((L) * 1.3))

#define MISSILE_MAX_SPD       100 /* maximum possible missile speed */
#define MISSILE_MIN_SPD       40 /* minimum missile speed */
#define MISSILE_VELOCITY(T,L) (((MISSILE_MAX_SPD - MISSILE_MIN_SPD) \
                               * bias( 0.70, ((L)/100.0) ) \
                               + MISSILE_MIN_SPD) * MISSILE_SPEED_MULT(T))
  
#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, 3 )
#define PHASE_MAX_MISSILES     20 /* maximum per phase at the highest waves  */
#define PHASE_MIN_MISSILES     3  /* minimum per phase at the highest waves  */
#define PHASE_VAR_MISSILES     6  /* phase has x+PHASE_VAR_MISSILES missiles */
#define PHASE_NUMBER_MISSILES(L) ((PHASE_MAX_MISSILES - PHASE_MIN_MISSILES) \
                                 * bias( 0.70, (L)/100.0 ) \
                                 + PHASE_MIN_MISSILES \
                                 + random() % PHASE_VAR_MISSILES)
  
/* 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_WAVE_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_NEW_BASE_AT        20000

#define POINTS_DEFAULT_HIGH_SCORE  1000

  
#define RCFILE_FILENAME_LENGTH      512 /* should prolly use fs limit, but */
#define RCFILE_MAX_LINE_SIZE        512 /* can't be bothered looking it up */
#define RCFILE_HIGHSCORE            "Highscore"
#define RCFILE_DATADIR              "Data"

  
#define ARGV_FULLSCREEN             "--fullscreen"
#define ARGV_FULLSCREEN_SHORT       "-f"
#define ARGV_SET_DATADIR            "--set-datadir"
#define ARGV_SET_DATADIR_SHORT      "-d"
#define ARGV_BIG_CURSOR             "--bigcursor"
#define ARGV_BIG_CURSOR_SHORT       "-b"

  
#define SCREENSHOT_FORMAT           "missile%03d.bmp"
  

  

const SDLKey QUIT                 = SDLK_ESCAPE;
const SDLKey STARTGAME            = SDLK_RETURN;
const SDLKey SCREENSHOT           = SDLK_PRINT;
const SDLKey PAUSE                = SDLK_PAUSE;
  
/* cheats */
const SDLKey SKIPWAVE             = 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 }; // <-- rocket science
#define CROSSHAIR_W              8
#define CROSSHAIR_H              8
#define CROSSHAIR_HOT_X          3
#define CROSSHAIR_HOT_Y          2
/*
Uint8 CURSOR[]  = { 0, 0, 0, 0, 
                    6, 96, 8, 16, 16, 8, 32, 4, 32, 4,
                    0, 0, 0, 0, 
                    32, 4, 32, 4, 16, 8, 8, 16, 6, 96,
                    0, 0, 0, 0 }; 
Uint8 MASK[]    = { 1, 128, 1, 128, 1, 128, 1, 128, 1, 128, 1, 128, 1, 128, 
                    254, 127, 254, 127, 
                    1, 128, 1, 128, 1, 128, 1, 128, 1, 128, 1, 128, 1, 128 }; 
*/
Uint8 BIG_CURSOR[] = { 0, 0, 0, 0, 0, 0, 0, 0, 8, 16, 4, 32, 2, 64, 0, 0,
                       0, 0, 2, 64, 4, 32, 8, 16, 0, 0, 0, 0, 0, 0, 0, 0 };
Uint8 BIG_MASK[]   = { 1, 128, 1, 128, 1, 128, 1, 128, 1, 128, 1, 128, 1, 128,
                       254, 127, 254, 127,
                       1, 128, 1, 128, 1, 128, 1, 128, 1, 128, 1, 128, 1, 128};
#define BIG_CROSSHAIR_W              16
#define BIG_CROSSHAIR_H              16
#define BIG_CROSSHAIR_HOT_X          7
#define BIG_CROSSHAIR_HOT_Y          7


/* colour info (for declaring SDL_Color structs and SDL_MapRGB() calls) */
typedef enum { RED, GREEN, BLUE, WHITE, BLACK, YELLOW, ALPHA } GameColour;
#define G_NUMBER_COLOURS  7  
#define G_RED           0xde,0x34,0x32
#define G_GREEN         0x48,0xd9,0x72
#define G_BLUE          0x9c,0x94,0xf4
#define G_WHITE         0xff,0xff,0xff
#define G_BLACK         0x00,0x00,0x00
#define G_YELLOW        0xff,0xff,0x00
#define G_ALPHA         0x00,0xff,0xff  /* ok, so its cyan.  Ssh, don't tell */
SDL_Color G_COLOUR[] = { {G_RED,   0x00}, {G_GREEN, 0x00}, {G_BLUE,   0x00},
                         {G_WHITE, 0x00}, {G_BLACK, 0x00}, {G_YELLOW, 0x00},
                         {G_ALPHA, 0x00} };

                         
/* images.  messy here, saves heaps of space later */
typedef enum {I_TITLE_MISSILECOMMAND, I_TITLE_ENTERTOSTART, I_GAMEOVER,
              I_WAVE, I_STATUS_HIGH, I_STATUS_SCORE, I_STATUS_WAVE, I_PAUSED,
              I_WAVE_NUMBER, I_EOW_WAVE, I_EOW_WAVE_NUMBER, I_EOW_COMPLETE,
              I_EOW_x10, I_EOW_x100, I_COLOUR_NUMBER, I_SAM_NUMBER,
              I_MISSILE, I_BASE, I_CITY, I_SMART, I_SMARTMASK} ImageFileName;
#define NUMBER_SPRITES             21
#define NUMBER_STATIC_IMAGES        8
/* the first set have permanent positions, so we can set them statically */
#define IMG_TITLE_MISSILECOMMAND   "missilecommand.png"
#define IMG_TITLE_ENTERTOSTART     "entertostart.png"
#define IMG_GAMEOVER               "gameover.png"
#define IMG_WAVE                   "intro_wave.png"
#define IMG_STATUS_HIGH            "status_high.png"
#define IMG_STATUS_SCORE           "status_score.png"
#define IMG_STATUS_WAVE            "status_wave.png"
#define IMG_PAUSED                 "pause.png"
/* the rest have positions calculated at runtime */
#define IMG_WAVE_NUMBER            "number-34x49.png"
#define IMG_EOW_WAVE               "eow_wave.png"
#define IMG_EOW_WAVE_NUMBER        "number-14x20.png"
#define IMG_EOW_COMPLETE           "eow_complete.png"
#define IMG_EOW_x10                "x10.png"
#define IMG_EOW_x100               "x100.png"
#define IMG_COLOUR_NUMBER          "number-11x15.png"
#define IMG_SAM_NUMBER             "samcount.png"
#define IMG_MISSILE                "missile.png"
#define IMG_BASE                   "base.png"
#define IMG_CITY                   "city.png"
#define IMG_SMART                  "smartbomb.11x11.png"
#define IMG_SMARTMASK              "smartbombmask.11x11.png"
char *SPRITE_FILENAME[] = {IMG_TITLE_MISSILECOMMAND, IMG_TITLE_ENTERTOSTART,
                           IMG_GAMEOVER, IMG_WAVE, IMG_STATUS_HIGH,
                           IMG_STATUS_SCORE, IMG_STATUS_WAVE, IMG_PAUSED,
                           IMG_WAVE_NUMBER, IMG_EOW_WAVE, IMG_EOW_WAVE_NUMBER,
                           IMG_EOW_COMPLETE, IMG_EOW_x10, IMG_EOW_x100,
                           IMG_COLOUR_NUMBER, IMG_SAM_NUMBER, IMG_MISSILE,
                           IMG_BASE, IMG_CITY, IMG_SMART, IMG_SMARTMASK};


#define ICON_FILENAME              "missile_icon.png"
 
/* these could all be calculated from the image sizes, this is to save bother:
 * size of the numbers in the white/blue/red/green number image doodad */
#define NUMBER_COLOURED_W   11
#define NUMBER_COLOURED_H   15
/* big green number sizes */
#define NUMBER_WAVE_W       34
#define NUMBER_WAVE_H       49
#define NUMBER_EOW_WAVE_W   14
#define NUMBER_EOW_WAVE_H   20
/* numbers that go in the samsites */
#define NUMBER_SAM_W         8 
#define NUMBER_SAM_H        12

  

/*
 *  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; /* next blast in the linked list */
} Blast;

typedef struct _Missile_Tag
{
  MissileFlavour type;
  Uint32   created_at;
  Uint32   end_at;
  Point   *path;
  int      path_length;
  int      at;             /* current location (path index) */
  double   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 */
  int       totalmissiles;
  Missile  *missile;
  Missile  *sam;
  Blast    *blast;
} Wave;

typedef struct
{
  long int    score;
  int         wave_number;
  SamSite     base[3];
  City        city[6];
  int         reserve_cities;
  int         reserve_bases;
  Wave        currentwave;
  int         gameover;
  Uint32      diff_time; /* diff in ms between game time and SDL_GetTicks */
  Uint32      pause_time; /* when the game was (last) paused */
  char        is_paused;
  long int    highscore;
  char       *data_dir;
  GameColour  scorecolor;
} Game;



/*
 *  Function prototypes:
 */

double bias( double b, double t );

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, Game *g, 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, Uint32 time, Uint16 at_x, Uint16 at_y);
void fire_missile_from_xy(Missile **ml, int wave, 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 wave, Uint32 time, Missile *m );

Uint32 gap_for_next_missile( int wave );


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

  
void delete_used_missiles( Missile **missile_list );
void delete_finished_explosions( Blast **blast_list, Uint32 time );


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

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

void toggle_pause( SDL_Surface *screen, Game *g );
#define get_game_time(G) ((G)->is_paused?(G)->pause_time: \
                                         (SDL_GetTicks() - (G)->diff_time))

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

void load_images( Game *g );
void set_image_position( int image, int x, int y );

void initgame_data( Game *g );

void show_game_splash_screen( SDL_Surface *screen );
void show_wave_splash_screen( SDL_Surface *screen, int number );
void show_end_of_wave_splash_screen( SDL_Surface *screen, Game *g );
void show_game_over_screen( SDL_Surface *screen );

void initwave_data( Game *g, int wavenumber );
void freewave_data( Game *g );

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

void read_rc_file( Game *g );
void write_rc_file( Game *g );
char *get_data_from_etc();
void set_missile_icon( 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_ */
