#include <stdlib.h>
#include <stdio.h>
#include "allegro.h"
#include <dos.h>
#include <time.h>
#include <string.h>
#include <apvector.h>
#include <apstring.h>
#include <apstring.cpp>
#include <conio.h>             //mouse
#include <fstream.h>

#define Number_Of_Levels 4

BITMAP *buffer;
BITMAP *ground, *sky;
BITMAP *terrain;

BITMAP *factory[2], *hangar[2], *petrol[2], *wreck[2];
BITMAP *tank[2], *fTank[2];
BITMAP *tankFire, *fTankFire;
BITMAP *wreckage;
BITMAP *SGunB, *SGunG, *SGunD;
BITMAP *axisHit[2];
BITMAP *axisHitFlipped[2];

apvector<BITMAP *> gunAnim(7);

apvector<BITMAP *> debrisPic(6);
apvector<BITMAP *> origDebrisPic(6);
apvector<BITMAP *> smoke(9);   //20x19
apvector<BITMAP *> exp(24);    //64x62

BITMAP *bombBmp;

BITMAP *axis[2];
BITMAP *faxis[2];

BITMAP *snb;
BITMAP *snl;
BITMAP *sng;
BITMAP *losescreen;
BITMAP *winscreen;
PALETTE pal;
PALETTE losePal, winPal;

const int MAXX = 640;
const int MAXY = 480;
const int firelength = 30;

apstring statusBar;
int numDead = 0;

int scrollX;
int terrW, terrH;
int counter;

apvector<int> startXes;
apvector<int> startYes;
apvector<int *> scores;

bool heLost;

int planeX = 2350, planeY = 300;
int active;
int mode;
int iPic;
int numToKill = 0;
//int col1, col2, col3;  //Colors of the rainbow... err, explosion
apvector<int> expCol, smk;

MIDI *music;
SAMPLE *mGun;
SAMPLE *explode;
SAMPLE *aagun;
SAMPLE *prop;
SAMPLE *bombSnd;
SAMPLE *selectWav;

int *levelI;
int red;
int difficulty = -1;

volatile char on, off;

int checkCollision(int, int, int);
int checkPlaneCollision(int, int, int);
int checkPlaneCollision(int, int, int, int);
void spawnDebris(int, int, int, int, int);
void spawnBullets(int, int, int, int, int);

void initKeys();
apstring intToAps(int);
void finGraphics(apstring);

apvector<int> path;

bool splashMode = 1;
BITMAP *background;
BITMAP *selected;
int clickVal = -1;
bool drewIt = 0;
bool userPressedQuit = 0;

#define black 6
#define white 10

#define bigW 64
#define smW 20
#define smsW 20

//Notes about the GUI
//play[4].d1 is the index of the selected item on the list in the"Play" dialog
//           In other words, the pilot
//chooseLevel[3].d1 is the index of the selected item on the list in the
//           "Choose Level" dialog, in other words the level.

int bgDraw(int msg, DIALOG *d, int c) {
   if(msg == MSG_IDLE && !drewIt) {
       scare_mouse();
       blit(background, screen, 0, 0, 0, 0, 640, 480);
       unscare_mouse();
       drewIt = 1;
   }
   return D_O_K;
}                 //gui   //BITMAP *b = (BITMAP *)d->dp;

int myButton(int msg, DIALOG *d, int c) {
   int x = d->x, y = d->y;
   int btnH = 25, btnW = 120;
   if(msg == MSG_GOTMOUSE) {
       scare_mouse();
       masked_blit(selected, screen, x, y, x, y, btnW, btnH);           
       play_sample(selectWav, 200, 122, 1000, 0);  unscare_mouse();
   }
   else if(msg == MSG_LOSTMOUSE) {
       scare_mouse();
       blit(background, screen, x, y, x, y, btnW, btnH);
       unscare_mouse();
   }
   else if(msg == MSG_CLICK) {
       return D_CLOSE;
   }
   return D_O_K;
}                                                 //gui
                     
DIALOG main_menu[] =
{
   /* (dialog proc)     (x)   (y)   (w)   (h)   (fg)  (bg)  (key) (flags)  (d1)                    (d2)  (dp)              (dp2) (dp3) */
   { myButton,           285,  168,  46,  23,   255,  0,    NULL,  D_EXIT,       0,                      0,    NULL,     NULL, NULL  },
   { myButton,           268,  193,  75,  23,   255,  0,    NULL,  D_EXIT,       0,                      0,    NULL,     NULL, NULL  },
   { myButton,           250,  215,  110, 23,   255,  0,    NULL,  D_EXIT,       0,                      0,    NULL,     NULL, NULL  },
   { myButton,           284,  241,  49,  23,   255,  0,    NULL,  D_EXIT,       0,                      0,    NULL,     NULL, NULL  },
   { myButton,           273,  264,  68,  23,   255,  0,    NULL,  D_EXIT,       0,                      0,    NULL,     NULL, NULL  },
   { myButton,           284,  289,  47,  23,   255,  0,    NULL,  D_EXIT,       0,                      0,    NULL,     NULL, NULL  },
   { NULL,              0,    0,    0,    0,    0,    0,    0,    0,       0,                      0,    NULL,             NULL, NULL  }
};

/*char helpString[700] =
"Help:


CONTROLS:


Player 1            Player 2 (ON KEYPAD)
----------------------------------------
Accelerate: x       Accelerate: 8
Decelerate: z       Decelerate: 5
Pull up:    ,       Pull up:    4
Push down:  /       Push down:  6
Flip plane: .       Flip plane: .
Drop bomb:  b       Drop bomb:  Enter
Fire:       Space   Fire:       0

Misc
----
Quit:                   q
Screenshot:             s
Proceed (in multiplay): p

More information can be found in the readme.doc file.";
*/
char helpString[700] =
"CONTROLS:


Player 1
-----------------
Accelerate: x
Decelerate: z
Pull up:    ,
Push down:  /
Flip plane: .
Drop bomb:  b
Fire:       Space

NOTE - The acceleration and decceleration buttons, unlike the others, MUST BE TAPPED, not held!

Misc
----
Quit:                     q (in game)
Return to the main menu:  Escape (in game)
Screenshot:               s
See the readme for more information about taking screenshots.

Basic gameplay information and detailed help can be found in the readme file.";

char creditsString[600] =
"
CREDITS:


The people & felines who helped me make this game:

My cats, for their support and motivation:
Fuzzy & Zeke

David Clark, for creating the original Sopwith!

The Sopwith Development Team, for the (temporarily) lifted graphics, the great web resource, and the idea to make a sequel to Sopwith!

My friends, for support and beta testing:
Arseny Puntikov.
Tim Reilly.
Aleksandr Kivenson.
Liyun Niu.
Leonid Morozofsky.
Michael Bocchinfuso.
Kevin Lin.
";
char exitString[30] = "Okay";

DIALOG help[] =
{
   /* (dialog proc)     (x)   (y)   (w)   (h)   (fg)  (bg)  (key) (flags)  (d1)                    (d2)  (dp)              (dp2) (dp3) */
   { d_textbox_proc,     135,   134, 350,  245,  black,white,NULL,  0,       0,                     0,    helpString,       NULL, NULL  },
   { d_button_proc,      135,   380,  350,  45,   black,white,NULL,  D_EXIT,  0,                     0,    exitString,       NULL, NULL  },
//   { bgDraw,     0,    0,    0,  0,  255,  255,    0,    0,       0,                      0,    NULL,               NULL, NULL  },
   { NULL,              0,    0,    0,    0,    0,    0,    0,    0,       0,                      0,    NULL,             NULL, NULL  }
};

DIALOG credits[] =
{
   /* (dialog proc)     (x)   (y)   (w)   (h)   (fg)  (bg)  (key) (flags)  (d1)                    (d2)  (dp)              (dp2) (dp3) */
   { d_textbox_proc,     135,   134,  350,  245,  black,white,NULL,  0,       0,                     0,    creditsString,       NULL, NULL  },
   { d_button_proc,      135,   380,  350,  45,   black,white,NULL,  D_EXIT,  0,                     0,    exitString,       NULL, NULL  },
//   { bgDraw,     0,    0,    0,  0,  255,  255,    0,    0,       0,                      0,    NULL,               NULL, NULL  },
   { NULL,              0,    0,    0,    0,    0,    0,    0,    0,       0,                      0,    NULL,             NULL, NULL  }
};

char highScoresString[1000];

DIALOG highScores[] =
{
   /* (dialog proc)     (x)   (y)   (w)   (h)   (fg)  (bg)  (key) (flags)  (d1)                    (d2)  (dp)              (dp2) (dp3) */
   { d_textbox_proc,     50,   134,  540,  245,  black,white,NULL,  0,       0,                     0,    highScoresString,       NULL, NULL  },
   { d_button_proc,      50,   380,  540,  45,   black,white,NULL,  D_EXIT,  0,                     0,    exitString,       NULL, NULL  },
//   { bgDraw,     0,    0,    0,  0,  255,  255,    0,    0,       0,                      0,    NULL,               NULL, NULL  },
   { NULL,              0,    0,    0,    0,    0,    0,    0,    0,       0,                      0,    NULL,             NULL, NULL  }
};

struct scoreTally {
    apvector<int> levelScores;

    scoreTally() {
        levelScores.resize(Number_Of_Levels);
        for(int k = 0; k < levelScores.length(); k++) {
            levelScores[k] = 0;
        }
    }
    ~scoreTally() {
        levelScores.resize(0);
    }

    int total() {
        int sum = 0;
        for(int k = 0; k < levelScores.length(); k++) {
            sum += levelScores[k];
        }
        return sum;
    }
};

struct pilot {
    int level;
    int maxLevel;
    apstring name;
    char nameCh[80];
    scoreTally score;
    int difficulty;

    pilot() {level = maxLevel = 0; name = "Joe Six-Prop";}
    ~pilot() {}

    void doCh() {
        strcpy(nameCh, name.c_str());
    }

    void set(char *nN, int nL, scoreTally nS, int nD) {
        name = nN;
        maxLevel = nL;
        score = nS;
        difficulty = nD;
    }
    
    void set(apstring nN, int nL, scoreTally nS, int nD) {
        name = nN;
        maxLevel = nL;
        score = nS;
        difficulty = nD;
    }
    
    void set(char *nN) {
        name = nN;
        maxLevel = 1;
        difficulty = 5;
    }
    
    void set(apstring nN) {
        name = nN;
        maxLevel = 1;
        difficulty = 5;
    }
};

apvector<pilot> pilots;
apvector<pilot *> players;
pilot computerPlayer;

apstring spaces(int number) {
    apstring toReturn;
    for(int k = 0; k < number; k++) {
       toReturn += " ";
    }
    return toReturn;
}

void sortPilots() {
    pilot temp;
    int maxPos;
    for(int inPos = 0; inPos < pilots.length(); inPos++) {
        maxPos = inPos;
        for(int checkPos = inPos + 1; checkPos < pilots.length(); checkPos++) {
            if(pilots[checkPos].score.total() >= pilots[maxPos].score.total()) {
                maxPos = checkPos;
            }
        }
        temp = pilots[inPos];
        pilots[inPos] = pilots[maxPos];
        pilots[maxPos] = temp;
    }
    apstring hsString = "HIGH SCORES: \n\n";
    hsString += "RANK NAME            LEVEL DIFFICULTY          SCORE\n";
    hsString += "---- --------------- ----- ---------- --------------\n";
    for(int index = 0; index < pilots.length(); index++) {
        char buf0[80], buf1[80], buf2[80], buf3[80];
        sprintf(buf0, "%d", index + 1);
        sprintf(buf1, "%d", pilots[index].maxLevel);
        sprintf(buf2, "%d", pilots[index].difficulty);
        sprintf(buf3, "%d", pilots[index].score.total());
        hsString += buf0;
        hsString += spaces(5 - strlen(buf0));
        hsString += pilots[index].name;
        hsString += spaces(16 - pilots[index].name.length());
        //Starts being right - justified, spaces come before
        hsString += spaces(5 - strlen(buf1));
        hsString += buf1;
        hsString += spaces(11 - strlen(buf2));
        hsString += buf2;
        hsString += spaces(15 - strlen(buf3));
        hsString += buf3;
        hsString += "\n";
    }
    strcpy(highScoresString, hsString.c_str());
}

char *listbox_getter(int index, int *list_size)
{
   if (index < 0) {
      *list_size = pilots.length();
      return NULL;
   }
   else {
      pilots[index].doCh();
      return pilots[index].nameCh;
   }
}

char *listbox_difficulty(int index, int *list_size)
{
   if (index < 0) {
      *list_size = 11;
      return NULL;
   }
   else {
       switch(index) {
          case 0:
               return "No computer plane";
               break;
          case 1:
               return "Pathetically Easy";
               break;
          case 2:
               return "Extremely Easy";
               break;
          case 3:
               return "Very Easy";
               break;
          case 4:
               return "Easy";
               break;
          case 5:
               return "Intermediate";
               break;
          case 6:
               return "Fairly Hard";
               break;
          case 7:
               return "Hard";
               break;
          case 8:
               return "Very Hard";
               break;
          case 9:
               return "Extremely Difficult";
               break;
          case 10:
               return "Insanity";
               break;
       }
   }
}           //Gui

template <typename T>
void removeItem(apvector<T> &vector, int index) {
     for(int k = index; k < vector.length() - 1; k++) {
         vector[k] = vector[k+1];
     }
     if(vector.length() >= 1) vector.resize(vector.length() - 1);
}

int deletePilot(int , DIALOG *, int );
int myList(int , DIALOG *, int );
int updateInfo();
char pilotInfo[100] = "PILOT INFO:\nClick \"New Pilot\" to create a pilot.";
char noPilotString[100] = "PILOT INFO:\nClick \"New Pilot\" to create a pilot.";

DIALOG play[] =
{
   /* (dialog proc)     (x)   (y)   (w)   (h)   (fg)  (bg)  (key) (flags)  (d1)  (d2)  (dp)                    (dp2) (dp3) */
   { d_button_proc,      220,   371,  84,  45,   black,white,NULL,  D_EXIT,  0,    0,    (char *)"Proceed",        NULL, NULL  },
   { d_button_proc,      305,  371,  84,  45,   black,white,NULL,  D_EXIT,  0,    0,    (char *)"Back",        NULL, NULL  },
   { d_button_proc,      220,   134,  170, 45,   black,white,NULL,  D_EXIT,  0,    0,    (char *)"New Pilot",   NULL, NULL  },
   { d_textbox_proc,     220,   180,  170,  16,  black,white,NULL,  0,       0,    0,    (char *)"SELECT PILOT",       NULL, NULL  },
   { myList,             220,   195,  170, 80,  black,white, 0,     0,       0,    0,    listbox_getter,        NULL, NULL  },
   { d_textbox_proc,     220,   276,  170,  48,  black,white,NULL,  0,       0,    0,    pilotInfo,       NULL, NULL  },
   { deletePilot,        220,   325,  170, 45,   black,white,NULL,  0,       0,    0,    (char *)"Delete Pilot",NULL, NULL  },
//   { bgDraw,             0,    0,    0,   0,    255,  255,  0,     0,       0,    0,    NULL,                  NULL, NULL  },
//   { updateInfo,          0,    0,    0,   0,    255,  255,  0,     0,       0,    0,    NULL,                  NULL, NULL  },
   { NULL,               0,    0,    0,   0,    0,    0,    0,     0,       0,    0,    NULL,                  NULL, NULL  }
};
                     //gui
int myList(int msg, DIALOG *d, int c) {
    int returnVal = d_list_proc(msg, d, c);
    if(msg == MSG_START || msg == MSG_CLICK || msg == MSG_DCLICK || msg == MSG_WHEEL || msg == MSG_CHAR || msg == MSG_USER) {
        //Update...
        int ind = play[4].d1;
        if(pilots.length() == 0) return returnVal;
        //strcpy(pilotInfo, pilots[ind].name.c_str());
        apstring tmp;
        //ind = 4;
        char buf1[80], buf2[80], buf3[80];
        sprintf(buf1, "Level: %d", pilots[ind].maxLevel);
        sprintf(buf2, "Score: %d", pilots[ind].score.total());
        sprintf(buf3, "Difficulty: %d", pilots[ind].difficulty);
        //alert(buf1, buf2, buf3, "Okay", NULL, NULL, NULL);
        tmp = "PILOT INFO: \n";
        tmp += "Name: " + pilots[ind].name + "\n";
        tmp += buf1;
        tmp += "\n";
        tmp += buf2;
        tmp += "\n";
        tmp += buf3;
        strcpy(pilotInfo, tmp.c_str());
        broadcast_dialog_message(MSG_DRAW, c);
        //updateInfo;
    }
    return returnVal;
}

int deletePilot(int msg, DIALOG *d, int c) {
    if(msg == MSG_CLICK) {
        if(alert(0, "Are you sure you want to delete this pilot?", 0, "Yes, I never liked him!", "No, whoops!", 0, 0) == 1) {
            removeItem(pilots, play[4].d1);
            if(pilots.length() == 0) {
                strcpy(pilotInfo, noPilotString);
            }
            broadcast_dialog_message(MSG_DRAW, c);
            play[4].d1--;
            if(play[4].d1 == -1) play[4].d1 = 0;
            myList(MSG_USER, d, c);
        }
    }
    else {
        return d_button_proc(msg, d, c);
    }
}                  //gui

#define enter 13;

char enteredName[50];
                                 //gui       //ASCII
DIALOG choices[] =
{
   /* (dialog proc)     (x)   (y)   (w)   (h)   (fg)  (bg)  (key) (flags)  (d1)                    (d2)  (dp)              (dp2) (dp3) */
   { d_textbox_proc,     220,   134,  170,  17,  black,white,NULL,  0,       0,                     0,    (char *)"Name: ",  NULL, NULL  },
   { d_edit_proc,       270,   138,  110,  17,  black,white,NULL,  0,       15,                    0,    enteredName,       NULL, NULL  },
   { d_textbox_proc,     220,   154,  170,  50,  black,white,NULL,  0, 0,   0,    (char *)"Difficulty: ",       NULL, NULL  },
   { d_list_proc,       220,  168,  170,  189,    black,white,    0,    0,       0,                      0,    listbox_difficulty,   NULL, NULL  },
   { d_button_proc,      220,   371,  84,  45,   black,white,13,  D_EXIT,  0,                     0,    (char *)"Okay",       NULL, NULL  },
   { d_button_proc,      306,   371,  84,  45,  black,white,NULL,  D_EXIT,  0,                     0,    (char *)"Back",       NULL, NULL  },//   { bgDraw,     0,    0,    0,  0,  255,  255,    0,    0,       0,                      0,    NULL,               NULL, NULL  },
   { NULL,              0,    0,    0,    0,    0,    0,    0,    0,       0,                      0,    NULL,             NULL, NULL  }
};

char *listbox_mission(int index, int *list_size)
{
   if (index < 0) {
      *list_size = 4;
      return NULL;
   }
   else {
       switch(index) {
          case 0:
               return "Mountain Mission";
               break;
          case 1:
               return "Amphibious Assault";
               break;
          case 2:
               return "Tank Turmoil";
               break;
          case 3:
               return "Memorable Map";
               break;
          /*case 4:
               return "Easy";
               break;
          case 5:
               return "Intermediate";
               break;
          case 6:
               return "Fairly Hard";
               break;
          case 7:
               return "Hard";
               break;
          case 8:
               return "Very Hard";
               break;
          case 9:
               return "Extremely Difficult";
               break;
          case 10:
               return "Insanity";
               break;
          */
       }
   }
}           //Gui

char levelInfo[100] = "LEVEL INFO:\nClick a level to view level's info.";
int myList2(int , DIALOG *, int );

DIALOG chooseLevel[] =
{
   /* (dialog proc)     (x)   (y)   (w)   (h)   (fg)  (bg)  (key) (flags)  (d1)  (d2)  (dp)                    (dp2) (dp3) */
   { d_button_proc,      200,  371,  104,  45,   black,white,NULL,  D_EXIT,  0,    0,    (char *)"FLY!",        NULL, NULL  },
   { d_button_proc,      305,  371,  104,  45,   black,white,NULL,  D_EXIT,  0,    0,    (char *)"Back",        NULL, NULL  },
   { d_textbox_proc,     200,  135,  210,  16,  black,white,NULL,  0,       0,    0,    (char *)"SELECT MISSION",       NULL, NULL  },
   { myList2,            200,  150,  210, 170,  black,white, 0,     0,       0,    0,    listbox_mission,        NULL, NULL  },
   { d_textbox_proc,     200,  321,  210,  48,  black,white,NULL,  0,       0,    0,    levelInfo,       NULL, NULL  },
//   { bgDraw,             0,    0,    0,   0,    255,  255,  0,     0,       0,    0,    NULL,                  NULL, NULL  },
//   { updateInfo,          0,    0,    0,   0,    255,  255,  0,     0,       0,    0,    NULL,                  NULL, NULL  },
   { NULL,               0,    0,    0,   0,    0,    0,    0,     0,       0,    0,    NULL,                  NULL, NULL  }
};
                     //gui
int myList2(int msg, DIALOG *d, int c) {
    int returnVal = d_list_proc(msg, d, c);
    if(msg == MSG_START || msg == MSG_CLICK || msg == MSG_DCLICK || msg == MSG_WHEEL || msg == MSG_CHAR || msg == MSG_USER) {
        //Update...
        int ind = chooseLevel[3].d1;
        int chosenPilot = play[4].d1;
        //strcpy(pilotInfo, pilots[ind].name.c_str());
        apstring tmp;
        //ind = 4;
        char buf1[80], buf2[80], buf3[80];
        //sprintf(buf1, "Level: %d", pilots[].level);
        sprintf(buf1, "Best Score: %d", pilots[chosenPilot].score.levelScores[ind]);
        //alert(buf1, buf2, buf3, "Okay", NULL, NULL, NULL);
        int *temp;
        tmp = "LEVEL INFO: \n";
        tmp += "Name: ";
        tmp += listbox_mission(ind, temp);
        tmp += "\n";
        bool available = (ind + 1 <= pilots[chosenPilot].maxLevel);
        if(available) {
            tmp += buf1;
        }
        else {
            tmp += "LEVEL NOT UNLOCKED";
        }
        //tmp += "\n";
        //tmp += buf2;
        //tmp += "\n";
        //tmp += buf3;
        strcpy(levelInfo, tmp.c_str());
        broadcast_dialog_message(MSG_DRAW, c);
        //updateInfo;
    }
    return returnVal;
}

void removeUnderscores(apstring &string) {
    for(int k = 0; k < string.length(); k++) {
        if(string[k] == '_') string[k] = ' ';
    }
}

void addUnderscores(apstring &string) {
    for(int k = 0; k < string.length(); k++) {
        if(string[k] == ' ') string[k] = '_';
    }
}

void writePilots() {
    ofstream of;
    of.open("Pilots.dat");
    int numPilots = pilots.length();
    int numAtts = 4 * numPilots;
    int attribute = 0;
    int index = 0;
    of << numPilots << " ";
    for(int k = 0; k < numAtts; k++) {   //            4: difficulty
        attribute++;
        if(attribute == 5) {
            attribute = 1;
            index++;
        }
        if(attribute == 1) {
            apstring tmp = pilots[index].name;
            addUnderscores(tmp);
            of << tmp;
        }
        else if(attribute == 2) {
            of << pilots[index].maxLevel;
        }
        else if(attribute == 3) {
            for(int k = 0; k < pilots[index].score.levelScores.length(); k++) {
                of << pilots[index].score.levelScores[k];
                of << " ";
            }
            of << "-1";
        }
        else if(attribute == 4) {
            of << pilots[index].difficulty;
        }
        of << " ";
    }
    of.close();
}

void readPilots() {
    ifstream iff;
    iff.open("Pilots.dat");
    int numPilots;
    apstring name;
    int level;
    scoreTally score;
    int difficulty;
    iff >> numPilots;                    //Attributes: 1: name
    int numAtts = 4 * numPilots;         //            2: Level
    int attribute = 0;                   //            3: Score
    for(int k = 0; k <= numAtts; k++) {  //            4: difficulty
        attribute++;
        if(attribute == 5) {
            pilots.resize(pilots.length() + 1);
            //name = "Ach_No";
            removeUnderscores(name);
            pilots[pilots.length() - 1].set(name, level, score, difficulty);
            name = "";
            //char buf1[80], buf2[80], buf3[80];
            //char tmp[80];
            //sprintf(buf1, "%d", level);
            //sprintf(buf2, "%d", score);
            //sprintf(buf3, "%d", difficulty);
            //alert(buf1, buf2, buf3, "Okay", NULL, NULL, NULL);
            attribute = 1;
        }
        
        if(attribute == 1) {
            iff >> name;
        }
        else if(attribute == 2) {
            iff >> level;
        }
        else if(attribute == 3) {
            int justRead = 0;
            for(int k = 0; justRead != -1; k++) {
                iff >> justRead;
                if(justRead != -1) score.levelScores[k] = justRead;
            }
        }
        else if(attribute == 4) {
            iff >> difficulty;
        }
    }
    iff.close();
}

int doGui() {   //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
   int ret;
   readPilots();
   PALETTE guiPal;
   background = load_bitmap("newgui.pcx", guiPal);
   selected = load_bitmap("covtext.pcx", NULL);
   set_palette(guiPal);
   gui_fg_color = black;
   gui_bg_color = white;
   
   mainMenu:
   blit(background, screen, 0, 0, 0, 0, 640, 480);
   //void *playerT = init_dialog(main_menu, -1);
   //while (update_dialog(playerT) || key[KEY_ESC]) ;
   //clickVal = shutdown_dialog(player) + 1;
   //while((clickVal = do_dialog(main_menu, -1) +1) == 255) ;

   clickVal = 0;
   while(clickVal == 0) clickVal = do_dialog(main_menu, -1) + 1;
   
   if(clickVal == 6) {
       writePilots();
       //exit(99);   //  Quit
       return -1;
   }
   if(clickVal == 4) {
       do_dialog(help, -1);    //help
       drewIt = 0;
       goto mainMenu;           //gui
   }
   if(clickVal == 3) {
       //do_dialog(help, -1);   //High Scores
       sortPilots();
       ret = do_dialog(highScores, -1);
       drewIt = 0;
       goto mainMenu;           //gui
   }
   else if(clickVal == 1) {
       playMenu:               //Play
       if(mouse_y >= 134 && mouse_y < 180) position_mouse(mouse_x, 180);
       blit(background, screen, 0, 0, 0, 0, 640, 480);
       ret = do_dialog(play, -1);
       if(ret == 0) {
           //TIME TO FLY!!!!!!!!! :)
           if(pilots.length() == 0) {
               alert(0, "You have not selected a pilot.", 0, "Sorry!", 0, 0, 0);
               goto playMenu;
           }
           //exit(99);
           blit(background, screen, 0, 0, 0, 0, 640, 480);
           ret = do_dialog(chooseLevel, -1);
           bool available = (chooseLevel[3].d1 + 1 <= pilots[play[4].d1].maxLevel);
           if(ret == 0 && available) {      //Choose a level
               pilots[play[4].d1].level = chooseLevel[3].d1 + 1;
               set_palette(pal);
               return play[4].d1;         //REALLY GO FLYING! :-)
           }
           else if(ret == 0) {
               alert("You have not yet unlocked that level.", "You must beat all of the levels before", "it to be able to play on it.", "Okay", 0, 0, 0);
               goto playMenu;
           }
           else {
               goto playMenu;
           }
           //set_palette(pal);
           //return play[4].d1;
       }
       else if(ret == 2) {
           //New pilot, eh?

           scare_mouse();
           blit(background, screen, 0, 0, 0, 0, 640, 480);
           drewIt = 0;
           unscare_mouse();
           *enteredName = NULL;
           ret = do_dialog(choices, -1);   //Get name & difficulty
           if(ret == 4) {
               if(strlen(enteredName) == 0) {
                   alert(0, "You didn't enter a name.", 0, "Okay", 0, 0, 0);
               }
               else {
                   pilots.resize(pilots.length()+1);
                   scoreTally tmp;
                   pilots[pilots.length() - 1].set(enteredName, 1, tmp, choices[3].d1);
               }
           }
           //drewIt = 0;
           goto playMenu;
           //Go on to enjoy etc.
       }
       drewIt = 0;
       goto mainMenu;
   }
   else if(clickVal == 2) {
       //This is options. NOT YET IMPLEMENTED
       alert(0, "You have no options.", 0, "I submit", 0, 0, 0);
       drewIt = 0;
       goto mainMenu;
   }
   else if(clickVal == 5) {
       //Credits
       do_dialog(credits, -1);
       drewIt = 0;
       goto mainMenu;
   }
   writePilots();
   set_palette(pal);
   exit(clickVal);  //gui
}

//########################################################################

void initLevel(apstring lev) {
     BITMAP *useless;
     apstring palFile = "pal" + lev + ".pcx";
     apstring levFile = "lev" + lev + ".pcx";
     useless = load_bitmap(palFile.c_str(), pal);
     ground = load_bitmap(levFile.c_str(), NULL);
     terrW = ground->w; terrH = ground->h;
     buffer = create_bitmap(terrW, terrH);
     clear(buffer);
     terrain = create_bitmap(terrW, terrH);
     clear(terrain);
     finGraphics(lev);
}

void loadExp() {
     BITMAP *fexp = load_bitmap("exp.pcx", NULL);
     BITMAP *fsmk = load_bitmap("smoke.pcx", NULL);
     for(int k = 0; k < 24; k++) {
         exp[k] = create_bitmap(64, 62);
         blit(fexp, exp[k], k*64, 0, 0, 0, 64, 62);
     }
     for(int k = 0; k < 9; k++) {
         smoke[k] = create_bitmap(20, 19);
         blit(fsmk, smoke[k], k*20, 0, 0, 0, 20, 19);
     }
}

void initGraphics() {
     allegro_init();
     install_keyboard();
     sky = load_bitmap("sky.pcx", NULL);
     
     losescreen = load_bitmap("youlose.pcx", losePal);
     winscreen = load_bitmap("youwin.pcx", winPal);
     
     axis[1] = load_bitmap("axis.pcx", NULL);
     faxis[1] = load_bitmap("axis.pcx", NULL);
     clear(faxis[1]);
     draw_sprite_v_flip(faxis[1], axis[1], 0, 0);
     
     axis[0] = load_bitmap("axish.pcx", NULL);
     faxis[0] = load_bitmap("axish.pcx", NULL);
     clear(faxis[0]);
     draw_sprite_v_flip(faxis[0], axis[0], 0, 0);
     
     factory[1] = load_bitmap("factory.pcx", NULL);
     petrol[1] = load_bitmap("petrol.pcx", NULL);
     tank[1] = load_bitmap("tank.pcx", NULL);
     fTank[1] = create_bitmap(tank[1]->w, tank[1]->h);
     clear(fTank[1]);
     draw_sprite_h_flip(fTank[1], tank[1], 0, 0);
     wreck[1] = load_bitmap("wreck.pcx", NULL);
     hangar[1] = load_bitmap("hangar.pcx", NULL);
     
     wreckage = load_bitmap("debris.pcx", NULL);
     
     factory[0] = load_bitmap("factoryh.pcx", NULL);
     petrol[0] = load_bitmap("petrolh.pcx", NULL);
     tank[0] = load_bitmap("tankh.pcx", NULL);
     fTank[0] = create_bitmap(tank[0]->w, tank[0]->h);
     clear(fTank[0]);
     draw_sprite_h_flip(fTank[0], tank[0], 0, 0);
     wreck[0] = load_bitmap("wreckh.pcx", NULL);
     hangar[0] = load_bitmap("hangarh.pcx", NULL);
     
     bombBmp = load_bitmap("bomb.pcx", NULL);
     tankFire = load_bitmap("tankfire.pcx", NULL);
     fTankFire = create_bitmap(tankFire->w, tankFire->h);
     clear(fTankFire);
     draw_sprite_h_flip(fTankFire, tankFire, 0, 0);

     axisHit[0] = load_bitmap("axishith.pcx", NULL);
     axisHit[1] = load_bitmap("axishit.pcx", NULL);
     axisHitFlipped[0] = load_bitmap("axishith.pcx", NULL);
     axisHitFlipped[1] = load_bitmap("axishit.pcx", NULL);
     clear(axisHitFlipped[0]);
     draw_sprite_v_flip(axisHitFlipped[0], axisHit[0], 0, 0);
     clear(axisHitFlipped[1]);
     draw_sprite_v_flip(axisHitFlipped[1], axisHit[1], 0, 0);

     SGunB = load_bitmap("SGunB.pcx", NULL);
     SGunG = load_bitmap("SGunG.pcx", NULL);
     SGunD = load_bitmap("SGunD.pcx", NULL);

     gunAnim[0] = load_bitmap("SGunF1.pcx", NULL);
     gunAnim[1] = load_bitmap("SGunF2.pcx", NULL);
     gunAnim[2] = load_bitmap("SGunF3.pcx", NULL);
     gunAnim[3] = load_bitmap("SGunF4.pcx", NULL);
     gunAnim[4] = load_bitmap("SGunF5.pcx", NULL);
     gunAnim[5] = load_bitmap("SGunF6.pcx", NULL);
     gunAnim[6] = load_bitmap("SGunF7.pcx", NULL);

     debrisPic[3] = load_bitmap("debris1.pcx", NULL);
     debrisPic[4] = load_bitmap("debris2.pcx", NULL);
     debrisPic[5] = load_bitmap("debris3.pcx", NULL);
     origDebrisPic[3] = load_bitmap("debris1.pcx", NULL);
     origDebrisPic[4] = load_bitmap("debris2.pcx", NULL);
     origDebrisPic[5] = load_bitmap("debris3.pcx", NULL);
     debrisPic[0] = load_bitmap("debris1h.pcx", NULL);
     debrisPic[1] = load_bitmap("debris2h.pcx", NULL);
     debrisPic[2] = load_bitmap("debris3h.pcx", NULL);
     origDebrisPic[0] = load_bitmap("debris1h.pcx", NULL);
     origDebrisPic[1] = load_bitmap("debris2h.pcx", NULL);
     origDebrisPic[2] = load_bitmap("debris3h.pcx", NULL);

     loadExp();
     
     snb = load_bitmap("snb.pcx", NULL);
     sng = load_bitmap("sng.pcx", NULL);
     snl = load_bitmap("snl.pcx", NULL);
     
     install_timer();
     install_mouse();
     if (install_sound(DIGI_AUTODETECT, MIDI_AUTODETECT, "moo") != 0) {
         exit(1);
     }

     mGun = load_sample("mgun.wav");
     explode = load_sample("explode.wav");
     aagun = load_sample("aagun.wav");
     prop = load_sample("prop.wav");
     bombSnd = load_sample("bomb.wav");
     selectWav = load_sample("select.wav");

     music = load_midi("airforce.mid");
     if (!music) {
         exit(1);
     }
     
     srand(rawclock());

     set_gfx_mode(GFX_AUTODETECT, 640, 480, 640, 480);

     play_midi(music, TRUE);
     text_mode(-1);
     //finGraphics(lev);
}

void finGraphics(apstring lev) {
     apstring palFile = "pal" + lev + ".pcx";
     apstring levFile = "lev" + lev + ".pcx";
     if(ground == NULL) {
         BITMAP *useless;
         useless = load_bitmap(palFile.c_str(), pal);
         ground = load_bitmap(levFile.c_str(), NULL);
         terrW = ground->w;
         terrH = ground->h;
         buffer = create_bitmap(terrW, terrH);
         clear(buffer);
         terrain = create_bitmap(terrW, terrH);
         clear(terrain);
     }
     blit(sky, terrain, 0, 0, 0, 0, terrW, terrH);
     masked_blit(ground, terrain, 0, 0, 0, 0, terrW, terrH);
     set_pallete(pal);
     red = makecol8(255,0,0);
}

double calculateAngle(double x1, double y1, double x2, double y2) {
    double changex, changey;
    changex = x2 - x1;
    changey = y2 - y1;
    if(changex == 0) {
        if(changey < 0) return 192;
        else return 64;
    }
    if(changey == 0) {
        if(changex > 0) return 0;
        else return 128;
    }
    if(changex > 0) return atan(changey / changex);
    else return atan(changey / changex) + 128;
}

struct explosion {
       double x, y, life, lifeSpent, frame;
       double centerX, centerY;
       double w, h, ow, oh;
       int myNumFrames;
       int type;
       int size;
       apvector<BITMAP *> mySprites;

       explosion() {life=0;}
       ~explosion() {}

       void set(double newx, double newy, double newLife, apvector<BITMAP *> newSprites, int newSize) {
            centerX = newx;
            centerY = newy;
            mySprites = newSprites;
            size = newSize;
            ow = mySprites[0]->w;
            oh = mySprites[0]->h;
            w = size;
            h = (size/ow) * oh;
            x = centerX - (w/2);
            y = centerY - (h/2);
            myNumFrames = mySprites.length();
            life = newLife;            
            lifeSpent = 0;
       }

       int update() {
            lifeSpent++;
            frame = (lifeSpent / life) * myNumFrames;  //calculate frame
            if(frame >= myNumFrames) {
                erase();     //end
                return 1;
            }
            else return 0;
       }

       void erase() {
            blit(terrain, buffer, x, y, x, y, w, h);
       }

       void draw() {
            if(frame >= 0 && frame < mySprites.length()) masked_stretch_blit(mySprites[frame], buffer, 0, 0, ow, oh, x, y, w, h);
       }
};

apvector<explosion> explosions;

struct moveable {  //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
       double x, y;
       BITMAP *myImage;
       BITMAP *myOrigImage;
       BITMAP *rotated;
       int type;
       bool dead;
       int player;
       double speed;
       int angle;
       int height;
       int putX, putY;
       int blitX, blitY;
       int oldBX, oldBY;
       double cX, cY;
       int wid, hig;
       int p2x, p2y;
       int side;
       int countdown;
       
       moveable() {
           speed = 1;
       }
       
       ~moveable() {}

       int sq(int a, int b) {
           return a * a;
       }

       void set(int newType, int newX, int newPlayer, double newSpeed) {
           dead = 0;
           type = newType; x = newX; y = path[x];
           player = newPlayer;
           speed = newSpeed;
           if(type == 1) myImage = hangar[player];
           if(type == 2) myImage = petrol[player];
           if(type == 3) myImage = factory[player];
           if(type == 5) myImage = tank[player];
           if(type == 6) {
               myImage = fTank[player];
           }
           myOrigImage = myImage;
           hig = myImage->h;
           wid = myImage->w;
           int longest;
           if(wid > hig) longest = wid;
           else longest = hig;
           side = longest * 2 + 4;
           rotated = create_bitmap(side, side);
           clear(rotated);
           putX = putY = side / 2;
           cX = x; cY = y;
           angle = 0;
           countdown = -1;
       }

       void die() {
           if(dead == 0) {
               erase();
               if(player == 1) {
                   *scores[0] += 250;
                   *scores[1] -= 500;
               }
               else if(player == 0) {
                   *scores[0] -= 500;
                   *scores[1] += 250;
               }
               play_sample(explodesmall, 200, 123, 1000, 0);
               explosions.resize(explosions.length() + 1);
               explosions[explosions.length() - 1].set(x, y, 30, exp, bigW);
               spawnDebris(x + myImage->w/2, y, 5, 3, player);
               dead = 1;
           }
       }

       void draw() {
            if(!dead) {
                clear(rotated);
                pivot_sprite(rotated, myImage, putX, putY, 0, 0, itofix(angle));
                masked_blit(rotated, buffer, 0, 0, blitX, blitY, rotated->w, rotated->h);
            }
       }

       void erase() {
            if(!dead) blit(terrain, buffer, oldBX, oldBY, oldBX, oldBY, rotated->w, rotated->h);
       }

       int update() {
            if(countdown > 0) countdown--;
            else {
                myImage = myOrigImage;
                countdown = -1;
            }
            if(!dead) {
                cX += speed;
                if(cX >= path.length() || cX - 15 < 0) {
                    erase();
                    return 1;
                }
                cY = path[cX];

                angle = calculateAngle(cX - 15, path[cX-15], cX, cY);

                p2x = cX - cos(angle) * wid;
                p2y = cY - sin(angle) * wid;
            
                oldBX = blitX; oldBY = blitY;

                blitX = p2x - cos(angle+64) * hig - side / 2;
                blitY = p2y - sin(angle+64) * hig + 1 - side / 2;

                x = ((blitX + side/2) + cX) / 2;
                y = ((blitY + side/2) + cY) / 2;
                return 0;
            }
       }
};

apvector<moveable> moveables;

struct bomb {
       double x, y, oldx, oldy, xmom, ymom, angle;

       bomb() {}
       
       void operator=(bomb &r) {
            oldx = r.oldx;
            oldy = r.oldy;
            x = r.x;
            y = r.y;
            xmom = r.xmom;
            ymom = r.ymom;
            angle = r.angle;
       }

       void set(double newx, double newy, double newxmom, double newymom) {
            x = newx - bombBmp->w/2;
            y = newy - bombBmp->h/2;
            xmom = newxmom;
            ymom = newymom;
            oldx = x; oldy = y;
            angle = calculateAngle(x, y, x + xmom, y + ymom);
       }

       int update() {
            int checkX = x + bombBmp->w/2, checkY = y + bombBmp->h/2;
            if(checkCollision(checkX, checkY, 2) || checkPlaneCollision(checkX, checkY, 2)) {
                spawnBullets(x + bombBmp->w/2, oldy, 25, 4, 10);
                oldx = x; oldy = y;
                erase();
                return 1;
            }
            else {oldx = x; oldy = y;}
            x += xmom; y += ymom;
            ymom += .1;
            angle = calculateAngle(oldx, oldy, x, y);
            return 0;
       }

       void erase() {
            blit(terrain, buffer, oldx, oldy, oldx, oldy, bombBmp->w, bombBmp->h);
       }

       void draw() {
            rotate_sprite(buffer, bombBmp, x, y, itofix(angle));
       }
};

apvector<bomb> bombs;

struct debrisBit {
       double x, y, oldx, oldy, xmom, ymom;
       int myPic;
       int player;

       debrisBit() {}

       void set(double newx, double newy, double newxmom, double newymom, int player) {
            x = newx; y = newy;
            xmom = newxmom; ymom = newymom;
            myPic = (3 * player) + iPic++;
            if(iPic == 3) iPic = 0;
       }

       int update() {
            oldx = x; oldy = y;
            int checkX = x + wreckage->w/2, checkY = y + wreckage->h/2;
            if(checkCollision(checkX, checkY, 3) || checkPlaneCollision(checkX, checkY, 3)) {
                erase();
                return 1;
            }
            x += xmom; y += ymom;
            ymom += .1;
            return 0;
       }

       void erase() {
            blit(terrain, buffer, oldx, oldy, oldx, oldy, wreckage->w, wreckage->h);
       }

       void draw() {
            masked_blit(debrisPic[myPic], buffer, 0, 0, x, y, wreckage->w, wreckage->h);
       }
};

apvector<debrisBit> debris(0);

struct building {
       double x, y;
       int myOldImageHeight;
       BITMAP *myImage;
       int type;
       int player;
       bool dead, flipped;

       building() {}
       ~building() {}
       
       void set(int newType, int newX, int newY, int newplayer) {
           dead = 0;
           type = newType; x = newX; y = newY;
           player = newplayer;
           if(type == 1) myImage = hangar[player];
           if(type == 2) myImage = petrol[player];
           if(type == 3) myImage = factory[player];
           if(type == 5) myImage = tank[player];
           if(type == 6) {
               myImage = tank[player];
               //BITMAP *tmp = create_bitmap(myImage->w, myImage->h);
               //clear(tmp);
               //draw_sprite_h_flip(tmp, myImage, 0, 0);
               //blit(tmp, myImage, 0, 0, 0, 0, myImage->w, myImage->h);
               //destroy_bitmap(tmp);
           }
           myOldImageHeight = myImage->h;
           masked_blit(myImage, terrain, 0, 0, x, y, myImage->w, myImage->h);
           masked_blit(myImage, buffer, 0, 0, x, y, myImage->w, myImage->h);
       }

       void die() {
           if(dead == 0) {
               if(player == 1) {
                   *scores[0] += 250;
                   *scores[1] -= 500;
               }
               else if(player == 0) {
                   *scores[0] -= 500;
                   *scores[1] += 250;
               }
               play_sample(explode, 200, 123, 1000, 0);
               explosions.resize(explosions.length() + 1);
               explosions[explosions.length() - 1].set(x + myImage->w/2, y + myImage->h/2, 30, exp, bigW);
               dead = 1;
               blit(sky, terrain, x, y, x, y, myImage->w, myImage->h - 2);
               blit(sky, buffer, x, y, x, y, myImage->w, myImage->h - 2);
               myImage = wreck[player];
               /*myImage = create_bitmap(wreck[0]->w, wreck[0]->h);
               blit(wreck[player], myImage, 0, 0, 0, 0, wreck[0]->w, wreck[0]->h);
               */
               y += myOldImageHeight - myImage->h;
               masked_blit(myImage, terrain, 0, 0, x, y, myImage->w, myImage->h);
               masked_blit(myImage, buffer, 0, 0, x, y, myImage->w, myImage->h);
               if(type != 2) spawnDebris(x + myImage->w/2, y, 5, 3, player);
               else spawnDebris(x + myImage->w/2, y, 17, 5, player);
               if(player == 1) numDead++;
           }
       }
};

apvector<building> buildings(0);

struct bullet {
       double x, y, oldx, oldy, xmom, ymom, life;
       int player;
       bool dead;

       bullet() {
            dead = 0;
       }

       void set(double newx, double newy, double newxmom, double newymom, double newlife, int newPlayer) {
            x = newx; y = newy;
            xmom = newxmom; ymom = newymom;
            life = newlife;
            player = newPlayer;
       }

       void set(double newx, double newy, double newxmom, double newymom, double newlife) {
            x = newx; y = newy;
            xmom = newxmom; ymom = newymom;
            life = newlife;
            player = -1;
       }

       void operator=(bullet &r) {
            oldx = r.oldx;
            oldy = r.oldy;
            dead = r.dead;
            x = r.x;
            y = r.y;
            xmom = r.xmom;
            ymom = r.ymom;
            life = r.life;
       }

       int update() {
            oldx = x;
            oldy = y;
            life--;
            if(life <= 0 || checkCollision(x, y, 0) || checkPlaneCollision(x, y, 0, player)) {erase(); return 1;}            x+= xmom;
            y+= ymom;
            return 0;
       }

       void erase() {
            blit(terrain, buffer, oldx - 2, oldy - 2, oldx - 2, oldy - 2, 4, 4);
       }

       void draw() {
            circlefill(buffer, x, y, 1, 0);
       }
};

apvector<bullet> bullets;

struct smallGun {
       int x, y, angle;
       int coverW, blitX, blitY, centerX, centerY;
       int waiting;
       BITMAP *base;
       BITMAP *gun;
       BITMAP *rotated;
       bool dead, shotYet;
       int animFrame, animWait;

       smallGun() {dead = 0;}
       ~smallGun() {
           //destroy_bitmap(base);
           //destroy_bitmap(rotated);
           //destroy_bitmap(gun);
       }

       void set(int x, int y) {
            dead = 0;
            shotYet = 0;
            animFrame = 0;
            animWait = 0;
            x = x; y = y; angle = 0; waiting  = 10;
            coverW = SGunG->w + 4;
            base = create_bitmap(SGunB->w, SGunB->h);
            gun = create_bitmap(SGunG->w, SGunG->h);
            rotated = create_bitmap(coverW, coverW);
            clear(rotated);
            blit(SGunG, gunAnim[animFrame], 0, 0, 0, 0, SGunG->w, SGunG->h);
            blit(SGunB, base, 0, 0, 0, 0, SGunB->w, SGunB->h);
            blitY = y - coverW / 2;
            blitX = x + base->w / 2 - coverW / 2;
            masked_blit(base, terrain, 0, 0, x, y, base->w, base->h);
            masked_blit(base, buffer, 0, 0, x, y, base->w, base->h);
            blit(terrain, buffer, 0, 0, 0, 0, terrW, terrH);
            centerX = x + base->w / 2; centerY = y + base->h / 2;
       }

       void die() {
           if(dead == 0) {
               *scores[0] += 250;
               blit(SGunD, gun, 0, 0, 0, 0, SGunG->w, SGunG->h);
               erase();
               draw();
               dead = 1;
               masked_blit(rotated, terrain, 0, 0, blitX, blitY, coverW, coverW);
           }
       }
       
       void update() {
            if(!dead) {
                if(animWait == 0)
                {
                    if((animFrame != 6) || (planeX - blitX < 250 && planeX - blitX > -250))
                    {
                        animFrame++;
                        if(animFrame == 7) {animFrame = 0; shotYet = 0;}
                        blit(gunAnim[animFrame], gun, 0, 0, 0, 0, SGunG->w, SGunG->h);
                        animWait = 10 - (.5 * difficulty);
                    }
                }
                if(animWait > 0) animWait--;
                angle = calculateAngle(centerX, centerY, planeX, planeY);
                if(animFrame == 0 && !shotYet) {shoot(); shotYet = 1;}
            }
       }

       void shoot() {
            play_sample(aagun, 200, 123, 1000, 0);
            
            bullets.resize(bullets.length() + 1);
            int ind = bullets.length() - 1;
            bullets[ind].set(centerX + 25 * cos(angle), centerY - 7 + 25 * sin(angle), 4 * cos(angle), 4 * sin(angle), 2*firelength);
            //bombs.resize(bombs.length() + 1);
            //int ind = bombs.length() - 1;
            //bombs[ind].set(centerX + 25 * cos(angle), centerY - 7 + 25 * sin(angle), 7 * cos(angle), 7 * sin(angle));
       }

       void erase() {
            if(!dead) blit(terrain, buffer, blitX, blitY, blitX, blitY, coverW, coverW);
       }
       
       void draw() {
            if(!dead) {
                clear(rotated);
                rotate_sprite(rotated, gun, 2, coverW/2 - gun->h/2 - 2, itofix(angle));
                masked_blit(rotated, buffer, 0, 0, blitX, blitY, coverW, coverW);
            }
       }
};

apvector<smallGun> smallGuns(0);

void readBuildings(apstring file) {
     ifstream iff;
     int read=0, last=-1, type=0, property=1, x=0, y=0;
     int player = 1;
     iff.open(file.c_str());
     numToKill = 0;

     iff >> read;
     while(read != 0) {
         startXes.resize(startXes.length() + 1);
         startYes.resize(startYes.length() + 1);
         startXes[startXes.length() - 1] = read;
         iff >> read;
         startYes[startYes.length() - 1] = read;
         iff >> read;
     }

     while(last != read) {
         last = read;
         iff >> read;
         if(property == 1) {
             if(read != 999) {
                 property++;
                 type = read;
             }
             else {
                 player = 1 - player;
             }
         }
         else if(property == 2) {
             property++;
             x = read;
         }
         else if(property == 3) {
             property = 1;
             y = read;
             if(type != 4) {
                 buildings.resize(buildings.length() + 1);
                 buildings[buildings.length() - 1].set(type, x, y, player);
                 if(player == 1) numToKill++;
             }
             else {
                 smallGuns.resize(smallGuns.length() + 1);
                 smallGuns[smallGuns.length() - 1].set(x, y);
             }
         }
     }
     iff.close();
}

struct airplane {
       double x, y, oldx, oldy, goal, speed, realSpeed, engSpeed, stallSpeed, stalling;
       int angle;
       volatile char *lKey, *rKey, *uKey, *dKey, *fKey, *sKey, *bKey;
       double acceleration, maxSpeed, maxEng, minSpeed, chAngle;
       bool canAccel, started, canFlip, flipped, stopped;
       int waiting, bombwait, dead;
       int pW, pH;
       int blitX, blitY, blitH, blitW, rotX, rotY;
       int bombsLeft, bulletsLeft, livesLeft;
       int statusX;
       int statusY;
       BITMAP *rotated;
       BITMAP *myImage;
       BITMAP *status;
       int startX, startY;
       volatile char *OriglKey, *OrigrKey;
       int myIndex;
       int comp;
       bool justReset;
       bool playing;
       bool firstTime;
       int gunVoice, engVoice;
       int myColor;
       int agony;
       bool active;

       ~airplane() {
           //destroy_bitmap(rotated);
           //destroy_bitmap(status);
           if(!userPressedQuit) deallocate_voice(engVoice);
           if(!userPressedQuit) deallocate_voice(gunVoice);
       }

       void doVoices() {
           if(gunVoice != -1) {
               deallocate_voice(gunVoice);
               voice_set_playmode(gunVoice, PLAYMODE_PLAY);
               voice_set_frequency(gunVoice, 0);
           }
           if(engVoice != -1) {
               deallocate_voice(engVoice);
               voice_set_playmode(engVoice, PLAYMODE_PLAY);
               voice_set_frequency(engVoice, 0);
           }
           gunVoice = allocate_voice(mGun);
           if(gunVoice == -1) exit(33);
           voice_set_playmode(gunVoice, PLAYMODE_LOOP);
           voice_stop(gunVoice);
           if(!comp) {
               engVoice = allocate_voice(prop);
               if(engVoice == -1) exit(66);
               voice_set_playmode(engVoice, PLAYMODE_LOOP);
               voice_start(engVoice);
           }
           else engVoice = -1;
       }

       void reset() {
           agony = 0;
           if(firstTime != 0) firstTime = 1;
           initKeys();
           playing = 0;
           angle = 256-9; goal = 0; speed = 0; engSpeed = 0;
           stopped = 0;
           justReset = 1;
           flipped = 0;
           lKey = OriglKey;
           rKey = OrigrKey;
           acceleration = 200; maxSpeed = 6; maxEng = 4;
           chAngle = 3;
           canAccel = 1;
           canFlip = 1;
           realSpeed = speed;
           stallSpeed = 1;
           stalling = 0;
           waiting = 0;
           bombwait = 0;
           dead = 0;
           started = 0;
           blitX = 0;
           blitY = -10;
           blitW = 35;
           blitH = 36;
           x = startX; y = startY;
           y+=2;
           bombsLeft = 6;
           bulletsLeft = 100;
           status = load_bitmap("p1sf.pcx", NULL);
           if(firstTime) {
               gunVoice = engVoice = -1;
               doVoices();
           }
           voice_stop(gunVoice);
           firstTime = 0;
       }

       void init() {
            if(comp != 1) comp = 0;
            reset();
            myImage = axis[myColor];
            pW = axis[0]->w; pH = axis[0]->h;
            rotated = create_bitmap(blitW, blitH);
            rotX = blitW/2 - axis[0]->w/2;
            rotY = blitH/2 - axis[0]->h/2;
       }

       double pow(double num, int exp) {
              return num * num;
       }

       void makeCloser(int &thing, double goal, double by) {
            if(goal - thing > by) thing += by;
            else if(thing - goal > by) thing -= by;
            else thing = goal;
       }
       
       void makeCloser(double &thing, double goal, double by) {
            if(goal - thing > by) thing += by;
            else if(thing - goal > by) thing -= by;
            else thing = goal;
       }

       void stall() {
            if(speed < - maxSpeed / 2) speed = -maxSpeed / 2;
            y += stalling;
            if(stalling < 2) stalling += .1;
            makeCloser(angle, 64, .6);
       }

       void die() {
            if(dead == 0) {
                if(!started) started = 1;
                //lKey = rKey = bKey = sKey = fKey = &key[KEY_Q];
                explosions.resize(explosions.length() + 1);
                explosions[explosions.length() - 1].set(x + axis[0]->w/2, y + axis[0]->w/2, 20, exp, bigW);
                
                if(flipped) {
                    volatile char *temp = lKey;
                    lKey = rKey;
                    rKey = temp;
                    flipped = 0;
                    myImage = axisHitFlipped[myColor];
                }
                else {
                    myImage = axisHit[myColor];
                }
                //spawnDebris(x + axis->w/2, y - 10, 10, 4);
                dead = 1;
            }
       }

       int max(int a, int b) {
            if(a > b) return a;
            return b;
       }

       void doAgony() {
            started = 0;
            if(agony == 50 || rand() % 20 == 5) {
                explosions.resize(explosions.length() + 1);
                explosions[explosions.length() - 1].set(x + axis[0]->w/2, y + axis[0]->h/2, 8, exp, smW);
            }
            agony--;
            if(counter % 2 == 0) {
                explosions.resize(explosions.length() + 1);
                explosions[explosions.length() - 1].set(x + axis[0]->w/2 + ((rand() % 20) - 10), y + axis[0]->h/2 + ((rand() % 20) - 10), 5, exp, smW);
            }
            if(agony == 0) {
                init();
                int lw = (snl->w / 4) + 1;
                masked_blit(snl, status, (livesLeft) * lw, 0, (livesLeft) * lw + 158, 27, (4 - (livesLeft)) * lw, snl->h);
                if(livesLeft == -1 && !comp) {
                    heLost = 1;
                }
            }
       }

       void doDeath(int pX, int pY) {
            int result = max(checkCollision(pX, pY, 1), checkPlaneCollision(pX, pY, 1, myIndex));
            if(result == 1)
                die();
            else if(result == 2 && agony == 0) {
                if(flipped) {
                    volatile char *temp = lKey;
                    lKey = rKey;
                    rKey = temp;
                    flipped = 0;
                }
                if(!(x < startX + 100 && x > startX - 100) || dead) {
                    *scores[myIndex] -= 500;  //IF HE ISN'T ON THE BASE OR HE'S DEAD
                    //erase();
                    //init();
                    int lw = (snl->w / 4) + 1;
                    masked_blit(snl, status, (livesLeft-1) * lw, 0, (livesLeft-1) * lw + 158, 27, (4 - (livesLeft - 1)) * lw, snl->h);
                    livesLeft--;
                    if(!comp) agony = 50;
                    else {
                       if(active) init();
                    }
                }
                else {             //IF HE IS ON THE BASE
                    init();
                    int lw = (snl->w / 4) + 1;
                    masked_blit(snl, status, livesLeft * lw, 0, livesLeft * lw + 158, 27, (4 - livesLeft) * lw, snl->h);
                }
            }
            else if(result == 3) {
                speed = 0;
                //engSpeed = 0;
            }
       }

       void checkDeath() {
            for(int k = -5; k <= 5; k+= 5) {
                int pX = x + axis[0]->w / 2, pY = y + axis[0]->h / 2;
                pX = pX + (cos(angle) * (pW/2 + 1));//plane->w / 2 + 5));
                pY = pY + (sin(angle) * (pW/2 + 1));//plane->h / 2 + 5));
                pX = pX + (sin(256-angle) * k);
                pY = pY + (cos(256-angle) * k);
                doDeath(pX, pY);
            }
            for(int k = -5; k <= 3; k+=4) {
                int pX = x + pW / 2, pY = y + pH / 2;
                pX = pX - (cos(angle) * (pW/2 + 1));//plane->w / 2 + 5));
                pY = pY - (sin(angle) * (pW/2 + 1));//plane->h / 2 + 5));
                pX = pX + (sin(256-angle) * k);
                pY = pY + (cos(256-angle) * k);
                doDeath(pX, pY);
            }
       }

       void update() {
            if(agony > 0) doAgony();
            justReset = 0;
            //if(engVoice == -1 && !comp) engVoice = play_sample(prop, 100, 123, 1000, 1);
            if(voice_check(engVoice) != prop) {
                doVoices();
            }
            if(!comp) voice_set_frequency(engVoice, (speed / 5.0 + 1.0) * 8000.0);
            if(dead && counter % 5 == 0) {
                explosions.resize(explosions.length() + 2);
                explosions[explosions.length() - 2].set(x + axis[0]->w/2, y + axis[0]->w/2, 20, smoke, smsW);
                explosions[explosions.length() - 1].set(x + axis[0]->w/2 + ((rand() % 40) * cos(angle)), y + axis[0]->h/2 + ((rand() % 20) * sin(angle)), 5, exp, smW);
            }

            if(waiting > 0) waiting--;
            if(bombwait > 0) bombwait--;

            if(dead) {
                if(angle < 64) angle++;
                if(angle > 64 && angle < 192) angle--;
                if(angle > 192) angle++;
                if(speed > 3) speed-=.1;
            }

            double sinner = sin(angle);
            oldx = x;
            oldy = y;
            if(started) {
                x = x + speed * cos(angle);
                y = y + speed * sin(angle);   //ADD *2 at the end for FUN
                speed += sinner / 5;                        //5 IS GOOD
            }
            if(!comp || dead != 0) makeCloser(speed, engSpeed, engSpeed / 40); //40 IS GOOD
            else makeCloser(speed, engSpeed, engSpeed / 20); //40 IS GOOD
            if(speed > maxSpeed) speed = maxSpeed;
            else if(speed < .5 && started) stall();
            else stalling=0;
       }

       void shoot() {
            if(bulletsLeft > 0) {
                int locx = x, locy = y;
                locx += 17; locy += 7;
                locx = locx + ((2 * speed) + 15) * cos(angle);
                locy = locy + ((2 * speed) + 15) * sin(angle);
                bullets.resize(bullets.length() + 1);
                bullets[bullets.length()-1].set(locx, locy, cos(angle) * (speed+6), sin(angle) * (speed+6), firelength, myIndex);
                waiting = 5;
                int numBigs = (bulletsLeft - 1 + 10) / 20;
                if(numBigs != (bulletsLeft + 10)/20) {
                    int bw = (sng->w / 5) + 1;
                    masked_blit(sng, status, numBigs * bw, 0, numBigs * bw + 105, 26, bw, sng->h);
                }
            }
            if(!comp) bulletsLeft--;
       }

       void dropBomb() {
            if(bombsLeft > 0) { // || 1) {
                play_sample(bombSnd, 200, 123, 1000, 0);
                int locx = x, locy = y;
                locx += 17; locy += 7;
                if(!flipped) {
                    locx = locx + 15 * cos(angle + 64);
                    locy = locy + 15 * sin(angle + 64);
                }
                else {
                    locx = locx - 15 * cos(angle + 64);
                    locy = locy - 15 * sin(angle + 64);
                }
                bombs.resize(bombs.length() + 1);
                bombs[bombs.length() - 1].set(locx, locy, cos(angle) * speed, sin(angle) * speed);
                bombwait = 50;
                int bw = (snb->w / 6) + 1;
                masked_blit(snb, status, (bombsLeft-1) * bw, 0, (bombsLeft-1) * bw + 7, 26, bw, snb->h);
            }
            bombsLeft--;
       }

       void getKeys() {
            if(*uKey && !dead) {
                if(canAccel && engSpeed <= maxEng && started) {engSpeed++; canAccel = 0;}
                else if(!started && agony == 0) {engSpeed = speed = 2; canAccel = 0; started = 1; angle = 0;y-=2;}
            }
            else if(*dKey  && !dead) {
                if(canAccel && engSpeed >= 1) {engSpeed--; canAccel = 0;}
            }
            else canAccel = 1;
            if(*sKey && !waiting && !dead) {
                if(voice_check(gunVoice) != mGun) doVoices();
                voice_start(gunVoice);// = play_sample(mGun, 255, 123, 1000, 1);
                shoot();
                //dropBomb();
            }
            else if(!*sKey || dead) {
                voice_stop(gunVoice);
            }
            
            if(*bKey && !bombwait && !dead) dropBomb();

            int angleMod = int(angle) % 16;

            if(*lKey && started && !dead) {
                if(goal == 0) {
                    if(angleMod != 0) goal = -angleMod;
                    else goal = -16;
                }
            }
            else if(*rKey && started && !dead) {
                if(goal == 0) {
                    if(angleMod != 0) goal = angleMod;
                    else goal = 16;
                }
            }

            if(*fKey && canFlip && !dead) {
                volatile char *tmpC;
                if(myImage == axis[myColor]) myImage = faxis[myColor];
                else myImage = axis[myColor];
                canFlip = 0;
                tmpC = lKey;
                lKey = rKey;
                rKey = tmpC;
                flipped = 1 - flipped;
            }
            else if(!*fKey) canFlip = 1;
            if(goal > chAngle) {angle+= chAngle; goal -= chAngle;}
            else if(goal < -chAngle) {angle -= chAngle; goal += chAngle;}
            else {angle += goal; goal=0;}

            if(angle < 0) angle += 256;
            else if(angle > 256) angle -= 256;
       }

       void erase() {
            blit(terrain, buffer, oldx, oldy - 10, oldx, oldy - 10, blitW, blitH);
       }

       void draw() {
            if(started) checkDeath();
            if(agony == 0)  {
                clear(rotated);
                rotate_sprite(rotated, myImage, rotX, rotY, itofix(angle));
                masked_blit(rotated, buffer, 0, 0, x - rotX, y - rotY, blitW, blitH);
            }
       }
};

apvector<airplane> planes(0);

void spawnDebris(int x, int y, int howMany, int force, int player) {
     debris.resize(debris.length() + howMany);
     int increment = 256 / howMany;
     for(int angle = 0; angle < howMany * increment; angle+= increment) {
         debris[debris.length() - (angle / increment) - 1].set(x, y, force * cos(angle), force * sin(angle), player);
     }
}

void spawnBullets(int x, int y, int howMany, int force, int life) {
     bullets.resize(bullets.length() + howMany);
     int increment = 256 / howMany;
     for(int angle = 0; angle < howMany * increment; angle+= increment) {
         bullets[bullets.length() - (angle / increment) - 1].set(x, y, force * cos(angle), force * sin(angle), life);
     }
}

void closeGraphics() {
     /*for(int k = 0; k <= 1; k++) {
         destroy_bitmap(axis[k]);
         destroy_bitmap(axis[k]);
         destroy_bitmap(hangar[k]);
         destroy_bitmap(tank[k]);
         destroy_bitmap(petrol[k]);
         destroy_bitmap(factory[k]);
     }
     destroy_bitmap(ground);
     destroy_bitmap(sky);
     destroy_midi(music);
     destroy_sample(mGun);
     destroy_sample(prop);
     destroy_sample(explode);
     destroy_sample(aagun);*/
}

struct computer {
       int index, cruise, target;
       bool oscillate, faceLeft, faceRight, eFaceLeft, eFaceRight;
       volatile char lKey, rKey, uKey, dKey, fKey, sKey, bKey;
       double *x, *y, *speed;
       int *angle;
       int goal, chAngle, centerx;
       int state;
       bool myFlipped;
       bool cruised;
       int angleToEnemy;
       bool active;
       int timeToShoot, maxTimeToShoot;
       
       computer() {
            cruise = 50;
            target = 0;
            state = 0;
            oscillate = 0;
       }

       void set(int moo) {
            index = moo;
            cruised = 0;
            chAngle = 16;
            myFlipped = 0;
            x = &planes[index].x;
            y = &planes[index].y;
            angle = &planes[index].angle;
            speed = &planes[index].speed;
            faceLeft = 0;
            faceRight = 1;
            lKey = rKey = uKey = dKey = fKey = sKey = bKey = off;
            planes[index].OriglKey = &lKey;
            planes[index].OrigrKey = &rKey;
            planes[index].lKey = &lKey;
            planes[index].rKey = &rKey;
            planes[index].uKey = &uKey;
            planes[index].dKey = &dKey;
            planes[index].fKey = &fKey;
            planes[index].sKey = &sKey;
            planes[index].bKey = &bKey;
            planes[index].speed = 6;
            maxTimeToShoot = 100 - (10 * difficulty);
            timeToShoot = maxTimeToShoot;
       }

       void updateVars() {
            faceLeft = (*angle >= 64 && *angle <= 192);
            faceRight = !faceLeft;
            eFaceLeft = (planes[target].angle >= 64 && planes[target].angle < 192);
            eFaceRight = !eFaceLeft;
            oscillate = 1 - oscillate;
            centerx = *x + axis[0]->w / 2;
            if(planes[index].myImage == faxis[planes[index].comp]) myFlipped = 1;
            else myFlipped = 0;
            angleToEnemy = calculateAngle(*x, *y, planes[target].x, planes[target].y);
            if(timeToShoot > 0) timeToShoot--;
       }

       int rot(int angle) {
            if(angle < 0) return angle + 256;
            else if(angle > 256) return angle - 256;
            else return angle;
       }

       int calcDiff(int a, int b) {
            int d1 = abs(a - b);
            int d2 = 256 - d1;
            if(d1 < d2) {
                if(a > b) return -d1;
                else return d1;
            }
            else {
                if(a < b) return -d2;
                else return d2;
            }
       }
       
       void update() {
            updateVars();
            lKey = off;
            rKey = off;
            uKey = off;
            fKey = off;
            sKey = off;

            uKey = on;
            
            if(difficulty >= 6 && planes[index].engSpeed < 5) planes[index].engSpeed = 5; //uKey = on; //*speed = 5;
            else if(planes[index].engSpeed < 4) planes[index].engSpeed = 4;

            if(faceLeft && !myFlipped) { // && oscillate) {
                fKey = on;
                //updateVars();
            }
            else if(faceRight && myFlipped) { // && oscillate) {
                fKey = on;
                //updateVars();
            }

            if(planes[index].justReset || planes[target].dead != 0) {cruised = 0;}
            //if(abs(planes[target].x - *x) > 300) cruised = 0;

            if(abs(planes[target].x - *x) > 240) timeToShoot = maxTimeToShoot;
            
            if(((difficulty != 10 && abs(goal) < 8) || (difficulty == 10 && abs(goal) < 20)) && cruised && abs(*x - planes[target].x) < 200 && timeToShoot == 0) {
                sKey = on;
            }

            if(!cruised) {
                if(*y < cruise) {
                    cruised = 1;
                    if(planes[target].x < *x) goal = calcDiff(*angle, 128);
                    else goal = calcDiff(*angle, 0);
                }
                else if(planes[target].x < *x) {
                    goal = calcDiff(*angle, 160);
                }
                else {
                    goal = calcDiff(*angle, 224);
                }
            }
            else if(difficulty <= 5 && getpixel(ground, *x + 40 * cos(*angle), *y + 40 * sin(*angle) + 80) != 0) {
                //if(faceLeft)
                goal = calcDiff(*angle, 192);
                //else goal = calcDiff(*angle, 224);
            }
            else {
                goal = calcDiff(*angle, angleToEnemy);
            }

            //statusBar = intToAps(goal);
            
            if(goal >= chAngle && !fKey) {
                if(faceLeft) lKey = on;
                else rKey = on;
                timeToShoot = maxTimeToShoot;
            }
            else if(goal <= -chAngle && !fKey) {
                if(faceLeft) rKey = on;
                else lKey = on;
                timeToShoot = maxTimeToShoot;
            }
            if(active) planes[index].getKeys();
            if(active) planes[index].update();
       }
       
       void erase() {
            planes[index].erase();
       }

       void draw() {
            planes[index].draw();
            if(planes[index].justReset) state = 0;
       }
};

apvector<computer> computers(0);

/*
template <typename T>
void removeItem(apvector<T> &vector, int index) {
     for(int k = index; k < vector.length() - 1; k++) {
         vector[k] = vector[k+1];
     }
     if(vector.length() >= 1) vector.resize(vector.length() - 1);
}
*/

void updateObjects() {
     for(int k = 0; k < debris.length(); k++) if(debris[k].update()) removeItem(debris, k);
     for(int k = 0; k < bullets.length(); k++) if(bullets[k].update()) removeItem(bullets, k);
     for(int k = 0; k < bombs.length(); k++) {
         if(bombs[k].update()) removeItem(bombs, k);
     }
     for(int k = 0; k < smallGuns.length(); k++) smallGuns[k].update();
     for(int k = 0; k < planes.length(); k++) if(!planes[k].comp) {planes[k].getKeys(); planes[k].update();}
     for(int k = 0; k < computers.length(); k++) computers[k].update();
     for(int k = 0; k < explosions.length(); k++) if(explosions[k].update()) removeItem(explosions, k);
     for(int k = 0; k < moveables.length(); k++) if(moveables[k].update()) removeItem(moveables, k);
}

void eraseObjects() {
     for(int k = 0; k < debris.length(); k++) debris[k].erase();
     for(int k = 0; k < bullets.length(); k++) bullets[k].erase();
     for(int k = 0; k < bombs.length(); k++) bombs[k].erase();
     for(int k = 0; k < smallGuns.length(); k++) smallGuns[k].erase();
     for(int k = 0; k < planes.length(); k++) if(!planes[k].comp) planes[k].erase();
     for(int k = 0; k < computers.length(); k++) computers[k].erase();
     for(int k = 0; k < explosions.length(); k++) explosions[k].erase();
     for(int k = 0; k < moveables.length(); k++) moveables[k].erase();
}

void drawObjects() {
     for(int k = 0; k < debris.length(); k++) debris[k].draw();
     for(int k = 0; k < bullets.length(); k++) bullets[k].draw();
     for(int k = 0; k < bombs.length(); k++) bombs[k].draw();
     for(int k = 0; k < smallGuns.length(); k++) smallGuns[k].draw();
     for(int k = 0; k < planes.length(); k++) if(!planes[k].comp) planes[k].draw();
     for(int k = 0; k < computers.length(); k++) computers[k].draw();
     for(int k = 0; k < explosions.length(); k++) explosions[k].draw();
     for(int k = 0; k < moveables.length(); k++) moveables[k].draw();
}

int checkCollision(int x, int y, int type) {  //TYPE 0: BULLET 1: PLANE 2: BOMB 3: DEBRIS
     for(int k = 0; k < buildings.length() && type != 3; k++) {
         if(getpixel(buildings[k].myImage, x - buildings[k].x, y - buildings[k].y) > 0) {
             buildings[k].die();
             if(type == 1 || type == 2) return 1;
             break;
         }
     }
     for(int k = 0; k < smallGuns.length() && type != 3; k++) {
         if(getpixel(smallGuns[k].rotated, x - smallGuns[k].blitX, y - smallGuns[k].blitY) > 0) {
             smallGuns[k].die();
             if(type == 1 || type == 2) return 1;
             break;
         }
     }
     for(int k = 0; k < moveables.length() && type != 3; k++) {
         if(getpixel(moveables[k].rotated, x - moveables[k].blitX, y - moveables[k].blitY) > 0) {
             moveables[k].die();
             if(type == 1 || type == 2) return 1;
             break;
         }
     }
     return 0;
}

int checkPlaneCollision(int x, int y, int type) {
     for(int index = 0; index < planes.length(); index++) {
         if(getpixel(planes[index].rotated, (x - planes[index].x) + planes[index].rotX, (y - planes[index].y) + planes[index].rotY) > 0) {
             planes[index].die();
             return 1;
         }
     }
     if(type != 2 && getpixel(ground, x, y) != 0) return 1;
     if(type == 2 && getpixel(ground, x, y) != 0) return 2;
     return 0;
}

int checkPlaneCollision(int x, int y, int type, int itsIndex) {
     if(getpixel(ground, x, y) != 0) {
         if(y > 0 ) return 2;
         else if(type != 0 && !planes[itsIndex].dead) return 3;
     }
     for(int index = 0; index < planes.length(); index++) {
         if(itsIndex != index && getpixel(planes[index].rotated, (x - planes[index].x) + planes[index].rotX, (y - planes[index].y) + planes[index].rotY) > 0) {
             planes[index].die();
             if(type != 0) planes[itsIndex].die();
         }
     }
     return 0;
}

void initKeys() {
     planes[0].startX = startXes[0];//2350; //Human.y = Human.oldy = 325;
     planes[0].startY = startYes[0];//334;
     planes[0].statusX = 0;
     planes[0].OriglKey = &key[KEY_COMMA];
     if(players[0]->name == "borker") planes[0].OriglKey = &key[KEY_M];
     planes[0].OrigrKey = &key[KEY_SLASH];
     planes[0].uKey = &key[KEY_X];
     planes[0].dKey = &key[KEY_Z];
     planes[0].sKey = &key[KEY_SPACE];
     planes[0].bKey = &key[KEY_B];
     planes[0].fKey = &key[KEY_STOP];
     planes[0].myColor = 0;
     if(planes.length() > 1) {
         planes[1].startX = startXes[1];//2935;
         planes[1].startY = startYes[1];//314;
         planes[1].myColor = 1;
         if(mode == 2) {
             planes[1].OriglKey = &key[KEY_4_PAD];
             planes[1].OrigrKey = &key[KEY_6_PAD];
             planes[1].uKey = &key[KEY_8_PAD];
             planes[1].dKey = &key[KEY_5_PAD];
             planes[1].sKey = &key[KEY_0_PAD];
             planes[1].bKey = &key[KEY_ENTER_PAD];
             planes[1].fKey = &key[KEY_2_PAD];
         }
     }
}

void initVariables() {
     initKeys();
     blit(terrain, buffer, 0, 0, 0, 0, terrW, terrH);
     iPic = 0;
     expCol.resize(3);
     smk.resize(3);
     for(int k = 0; k < planes.length(); k++) {
         planes[k].livesLeft = 4;
         planes[k].myIndex = k;
     }
     expCol[0] = makecol8(255, 0, 0);
     expCol[1] = makecol8(255, 123, 4);
     expCol[2] = makecol8(255, 245, 75);
     
     smk[2] = makecol8(160, 160, 160);
     smk[1] = makecol8(180, 180, 180);
     smk[0] = makecol8(200, 200, 200);
     counter = 0;
     set_keyboard_rate(0,0);
}

void initPlanes() {
     for(int index = 0; index < planes.length(); index++) {
         planes[index].myIndex = index;
         planes[index].init();
     }
     if(mode == 1) {
         computers[0].set(1);
         planes[0].statusX = 0;
         planes[0].statusY = 480 - planes[0].status->h;
     }
     else if(mode == 2) {
         planes[0].statusX = 0;
         planes[0].statusY = 480 - planes[0].status->h;
         planes[1].statusX = 0;
         planes[1].statusY = 240 - planes[0].status->h;
     }
}

apstring intToAps(int theInt) {
     char tmp[80];
     sprintf(tmp, "%d", theInt);
     return apstring(tmp);
}

void showStatuses() {
     apstring message;
     for(int k = 0; k < planes.length(); k++) {
         if(!planes[k].comp) {
             message = players[k]->name + ": " + intToAps(*scores[k]);
             if(statusBar != "") message = statusBar;
             blit(planes[k].status, screen, 0, 0, planes[k].statusX, planes[k].statusY, planes[k].status->w, planes[k].status->h);
             textout(screen, font, message.c_str(), planes[k].statusX + 10, planes[k].statusY + 8, red);
         }
     }
}

void doSplashScreen() {
     PALETTE splashPal;
     BITMAP *splash = load_bitmap("title.pcx", splashPal);
     set_palette(splashPal);
     blit(splash, screen, 0, 0, 0, 0, 640, 480);
     while(!keypressed() && !mouse_b & 1) ;
     clear_keybuf();
     set_palette(pal);
}

void readPath(apstring file) {
     ifstream iff;
     iff.open(file.c_str());
     
     int read = 0, last = -1, pos = 0;

     int tmp;
     iff >> tmp;
     path.resize(tmp);
     while(pos < tmp) {
         last = read;
         iff >> read;
         path[pos] = read;
         pos++;
     }
}

void checkMoveables() {
     if(moveables.length() > 0) {
         int checkX = moveables[0].x;
         for(int k = 0; k < buildings.length(); k++) {
             if(!buildings[k].dead && abs(checkX - (buildings[k].x + buildings[k].myImage->w/2)) < 50) {
                 if(buildings[k].player == 0) {
                     buildings[k].die();
                     if(moveables[0].myImage == fTank[1]) {
                         moveables[0].myImage = fTankFire;
                         moveables[0].countdown = 20;
                     }
                     else {
                         moveables[0].myImage = tankFire;
                         moveables[0].countdown = 20;
                     }
                 }
             }
         }
     }
}

void doDifficulty() {
     apstring input;
     while(!(mode == 1 || mode == 2)) {
         cout << "1 or 2 players? ";
         cin >> input;
         if(input == "1") {mode = 1; *levelI = 1;}
         else if(input == "2") {mode = 2; *levelI = 1;}
         else if(input == "moo") {mode = 1; *levelI = 3;}
         else if(input == "d") {mode = 1; *levelI = 1; difficulty = 5;}
     }
     if(difficulty == -1) {
         cout << "What level of difficulty? (1-10): ";
         cin >> difficulty;
         if(!(difficulty <= 10 && difficulty >= 0)) difficulty = 5;
     }
}

void resizePlanes() {
     planes.resize(2);
     scores.resize(2);
     on = '1', off = NULL;
     //scores[0] = scores[1] = 0;
     if(mode == 1) {
         computers.resize(1);
         planes[1].comp = 1;
     }
}

void initGraphicsAndSplashScreen() {
     initGraphics();
     if(splashMode == 1) doSplashScreen();
}

void readData() {
     readBuildings("lev" + intToAps(*levelI) + ".hex");
     readPath("path" + intToAps(*levelI) + ".pat");
}

int main() {
     mode = -1;
     bool firstRun = 1;
     int lastLevel = 3;
     heLost = 0;
     cout << endl;
     
     //doDifficulty();
     mode = 1;

     resizePlanes();
     
     initGraphicsAndSplashScreen();

     guiStart:
     
     pilots.resize(0);

     int temp = doGui();  //********************************************
     if(temp == -1) {
         userPressedQuit = 1;
         planes.resize(0);
         closeGraphics();
         exit(1);
     }
     players.resize(2);
     players[0] = &pilots[temp];
     players[1] = &computerPlayer;
     
     levelI = &(players[0]->level);
     
     apvector<int> maxScores(2);
     
     scores[0] = &(players[0]->score.levelScores[*levelI-1]);
     scores[1] = &(players[1]->score.levelScores[*levelI-1]);

     maxScores[0] = *scores[0];
     maxScores[1] = *scores[1];

     if(maxScores[0] < 0) maxScores[0] = 0;
     if(maxScores[1] < 0) maxScores[1] = 0;
     
     difficulty = players[0]->difficulty;

     initLevel(intToAps(*levelI));
     

     //if(firstRun)
     readData();
     initVariables();
     initPlanes();

     if(*levelI == 1 || difficulty == 0 || mode == 2) computers[0].active = 0;
     else computers[0].active = 1;
     if(mode == 1) planes[1].active = computers[0].active;
     else planes[1].active = 1;
     
     start:

     *scores[0] = *scores[1] = 0;

     if(*levelI == 3) {
         moveables.resize(6);
         moveables[5].set(6, 2500, 1, -1);
         moveables[4].set(6, 2450, 1, -1);
         moveables[3].set(6, 2400, 1, -1);
         moveables[2].set(6, 2350, 1, -1);
         moveables[1].set(6, 2300, 1, -1);
         moveables[0].set(6, 2250, 1, -1);
     }

     int debrisAngle = 0;

     planeX = planes[0].x + planes[0].pW/2 + 5 * planes[0].speed * cos(planes[0].angle);
     planeY = planes[0].y + planes[0].pH/2 + 5 * planes[0].speed * sin(planes[0].angle);

     int qKey = -1; //User has pressed escape or 'Q'

     do {
        statusBar = "";
        if(debris.length() > 0) {
            for(int index = 0; index < 6; index++) {
                clear(debrisPic[index]);
                rotate_sprite(debrisPic[index], origDebrisPic[index], 0, 0, itofix(debrisAngle));
            }
            debrisAngle+=3;
            if(debrisAngle > 256) debrisAngle -= 256;
        }

        planeX = planes[0].x + planes[0].pW/2 + 5 * planes[0].speed  * cos(planes[0].angle);
        planeY = planes[0].y + planes[0].pH/2 + 5 * planes[0].speed * sin(planes[0].angle);

        if(++counter == 101) counter = 1;
        
        updateObjects();
        checkMoveables();
        eraseObjects();
        int scrollY = 0;
        scrollX = planes[0].x - MAXX/2 + planes[0].pW/2;
        if(scrollX < 0) scrollX = 0;
        else if(scrollX > terrW - 640) scrollX = terrW - 640;
        drawObjects();

        vsync();
        
        if(mode == 2) {
            scrollY = planes[0].y + (axis[0]->h / 2) - 120;
            if(scrollY < 0) scrollY = 0;
            else if(scrollY > 240) scrollY = 240;
            blit(buffer, screen, scrollX, scrollY, 0, 0, 640, 239);
            scrollX = planes[1].x - MAXX/2 + planes[1].pW/2;
            scrollY = planes[1].y + (axis[0]->h / 2) - 120;
            if(scrollY < 0) scrollY = 0;
            else if(scrollY > 240) scrollY = 240;
            if(scrollX < 0) scrollX = 0;
            else if(scrollX > terrW - 640) scrollX= terrW - 640;
            blit(buffer, screen, scrollX, scrollY, 0, 241, 640, 239);
            //blit(p1sf, screen, 0, 0, 0, 480 - p1sf->h, p1sf->w, p1sf->h);
        }
        else if(mode == 1) {
            blit(buffer, screen, scrollX, 0, 0, 0, 640, 480);
            //blit(p1sf, screen, 0, 0, 0, 480 - p1sf->h, p1sf->w, p1sf->h);
        }
        showStatuses();
        
        if(key[KEY_S]) {
            save_bitmap("~sshotbuf.pcx", buffer, pal);
            BITMAP *tmp = create_bitmap(640, 480);
            blit(screen, tmp, 0, 0, 0, 0, 640, 480);
            save_bitmap("~sshotscr.pcx", tmp, pal);
            destroy_bitmap(tmp);
        }
        if( (mode == 1 && (numDead == numToKill || heLost)) || (mode == 2 && key[KEY_P]) ) {
            if(!heLost || mode == 2) {
                (*levelI)++;
                set_palette(winPal);
                blit(winscreen, screen, 0, 0, 0, 0, 640, 480);
                showStatuses();
                if(*levelI == 1 || difficulty == 0 || mode == 2) computers[0].active = 0;
                else computers[0].active = 1;
                if(mode == 1) planes[1].active = computers[0].active;
                else planes[1].active = 1;

                if(maxScores[0] > *scores[0]) *scores[0] = maxScores[0];
                if(maxScores[1] > *scores[1]) *scores[1] = maxScores[1];
                
                if(players[0]->score.levelScores.length() < *levelI) {
                    players[0]->score.levelScores.resize(*levelI);
                }
                if(players[1]->score.levelScores.length() < *levelI) {
                    players[1]->score.levelScores.resize(*levelI);
                }
                scores[0] = &(players[0]->score.levelScores[*levelI-1]);
                scores[1] = &(players[1]->score.levelScores[*levelI-1]);
                if(mode == 1) {
                    if(players[0]->maxLevel < *levelI) {
                        players[0]->maxLevel = *levelI;
                    }
                }
            }
            else {
                set_palette(losePal);
                stretch_blit(losescreen, screen, 0, 0, 256, 192, 0, 0, 640, 480);
                showStatuses();
                *scores[0] = 0;
            }
            clear_keybuf();
            readkey();
            for(;;) {
                if(key[KEY_Q] || *levelI > Number_Of_Levels) {
                    closeGraphics();
                    players[0]->score.levelScores.resize(Number_Of_Levels);
                    players[1]->score.levelScores.resize(Number_Of_Levels);
                    writePilots();
                    exit(55);
                }
                else if(keypressed()) {
                    clear(screen);
                    numDead = 0;
                    clear(buffer);
                    clear(terrain);
                    debris.resize(0);
                    bombs.resize(0);
                    bullets.resize(0);
                    explosions.resize(0);
                    moveables.resize(0);
                    destroy_bitmap(ground);
                    ground = NULL;
                    destroy_bitmap(buffer);
                    finGraphics(intToAps(*levelI));
                    buildings.resize(0);
                    smallGuns.resize(0);
                    startXes.resize(0);
                    startYes.resize(0);
                    readBuildings("lev" + intToAps(*levelI) + ".hex");
                    readPath("path" + intToAps(*levelI) + ".pat");
                    initPlanes();
                    initVariables();
                    if(mode == 1) computers[0].set(1);
                    heLost = 0;
                    goto start;
                }
            }
        }
        if(key[KEY_ESC]) qKey = 2;
        if(key[KEY_Q]) qKey = 1;
    } while(qKey == -1);


     if(qKey == 2) {    //Go back to gui
         //int temp = doGui();  //********************************************
         //players.resize(0);
         //players.resize(2);
         //players[0] = &pilots[temp];
         //players[1] = &computerPlayer;
         numDead = 0;
         firstRun = 0;
         players[0] = players[1] = NULL;
         planes.resize(0);
         resizePlanes();
         clear(buffer);
         clear(terrain);
         debris.resize(0);
         bombs.resize(0);
         bullets.resize(0);
         explosions.resize(0);
         moveables.resize(0);
         buildings.resize(0);
         smallGuns.resize(0);
         startXes.resize(0);
         startYes.resize(0);
         writePilots();
         goto guiStart;
     }
     closeGraphics();
     if(maxScores[0] > *scores[0]) *scores[0] = maxScores[0];
     if(maxScores[1] > *scores[1]) *scores[1] = maxScores[1];
     writePilots();
     return 0;
}
END_OF_MAIN();